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

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 (336) hide show
  1. package/LICENSE +46 -0
  2. package/README.md +2795 -2
  3. package/SKILL.md +106 -0
  4. package/ddd-skills/archi-code-ddd/imports.md +94 -0
  5. package/ddd-skills/archi-code-ddd/layers.md +314 -0
  6. package/ddd-skills/archi-code-ddd/patterns.md +683 -0
  7. package/ddd-skills/archi-code-ddd/what-not-to-do.md +196 -0
  8. package/ddd-skills/archi-code-ddd/where-each-class-goes.md +64 -0
  9. package/ddd-skills/ddd-agregados/GUIA.md +114 -0
  10. package/ddd-skills/ddd-agregados/references/consistencia-eventual.md +158 -0
  11. package/ddd-skills/ddd-agregados/references/ejemplo-ecommerce.md +52 -0
  12. package/ddd-skills/ddd-agregados/references/fabricas.md +193 -0
  13. package/ddd-skills/ddd-agregados/references/implementacion-ts.md +706 -0
  14. package/ddd-skills/ddd-arquitectura/GUIA.md +80 -0
  15. package/ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs +99 -0
  16. package/ddd-skills/ddd-arquitectura/references/estilos-adicionales.md +51 -0
  17. package/ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md +406 -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 +314 -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 +419 -0
  26. package/ddd-skills/ddd-context-mapping/references/estilos-integracion.md +152 -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 +476 -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 +130 -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 +529 -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 +314 -0
  38. package/ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md +191 -0
  39. package/ddd-skills/ddd-repositorios-persistencia/references/transacciones-ts.md +301 -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 +321 -0
  43. package/ddd-skills/ddd-servicios-capa-aplicacion/references/servicios-dominio-ts.md +190 -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 +218 -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 +376 -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/exceptions/aggregate-not-found.exception.d.ts +5 -0
  58. package/dist/cjs/application/exceptions/aggregate-not-found.exception.js +17 -0
  59. package/dist/cjs/application/exceptions/application-exception-code.d.ts +7 -0
  60. package/dist/cjs/application/exceptions/application-exception-code.js +11 -0
  61. package/dist/cjs/application/exceptions/application.exception.d.ts +6 -0
  62. package/dist/cjs/application/exceptions/application.exception.js +16 -0
  63. package/dist/cjs/application/exceptions/concurrency.exception.d.ts +5 -0
  64. package/dist/cjs/application/exceptions/concurrency.exception.js +17 -0
  65. package/dist/cjs/application/exceptions/not-allowed.exception.d.ts +5 -0
  66. package/dist/cjs/application/exceptions/not-allowed.exception.js +17 -0
  67. package/dist/cjs/application/exceptions/unexpected-error.exception.d.ts +5 -0
  68. package/dist/cjs/application/exceptions/unexpected-error.exception.js +17 -0
  69. package/dist/cjs/application/index.d.ts +14 -0
  70. package/dist/cjs/application/index.js +23 -0
  71. package/dist/cjs/application/ports/clock.d.ts +3 -0
  72. package/dist/cjs/application/ports/clock.js +2 -0
  73. package/dist/cjs/application/ports/logger.d.ts +8 -0
  74. package/dist/cjs/application/ports/logger.js +2 -0
  75. package/dist/cjs/application/use-case/use-case.d.ts +6 -0
  76. package/dist/cjs/application/use-case/use-case.js +25 -0
  77. package/dist/cjs/application/value-object-builder/value-object-builder.d.ts +16 -0
  78. package/dist/cjs/application/value-object-builder/value-object-builder.js +51 -0
  79. package/dist/cjs/domain/aggregate/aggregate-root.d.ts +9 -0
  80. package/dist/cjs/domain/aggregate/aggregate-root.js +17 -0
  81. package/dist/cjs/domain/entity/entity.d.ts +9 -0
  82. package/dist/cjs/domain/entity/entity.js +19 -0
  83. package/dist/cjs/domain/event/event-base.d.ts +23 -0
  84. package/dist/cjs/domain/event/event-base.js +21 -0
  85. package/dist/cjs/domain/exceptions/domain-exception-code.d.ts +7 -0
  86. package/dist/cjs/domain/exceptions/domain-exception-code.js +11 -0
  87. package/dist/cjs/domain/exceptions/domain.exception.d.ts +6 -0
  88. package/dist/cjs/domain/exceptions/domain.exception.js +16 -0
  89. package/dist/cjs/domain/exceptions/invalid-event-definition.exception.d.ts +4 -0
  90. package/dist/cjs/domain/exceptions/invalid-event-definition.exception.js +19 -0
  91. package/dist/cjs/domain/exceptions/required-value.exception.d.ts +4 -0
  92. package/dist/cjs/domain/exceptions/required-value.exception.js +20 -0
  93. package/dist/cjs/domain/exceptions/type-primitive.exception.d.ts +4 -0
  94. package/dist/cjs/domain/exceptions/type-primitive.exception.js +20 -0
  95. package/dist/cjs/domain/exceptions/validation.exception.d.ts +14 -0
  96. package/dist/cjs/domain/exceptions/validation.exception.js +23 -0
  97. package/dist/cjs/domain/index.d.ts +17 -0
  98. package/dist/cjs/domain/index.js +36 -0
  99. package/dist/cjs/domain/primitive/primitive-types.d.ts +20 -0
  100. package/dist/cjs/domain/primitive/primitive-types.js +2 -0
  101. package/dist/cjs/domain/repository/aggregate-finder.d.ts +4 -0
  102. package/dist/cjs/domain/repository/aggregate-finder.js +2 -0
  103. package/dist/cjs/domain/repository/aggregate-remover.d.ts +4 -0
  104. package/dist/cjs/domain/repository/aggregate-remover.js +2 -0
  105. package/dist/cjs/domain/repository/aggregate-saver.d.ts +4 -0
  106. package/dist/cjs/domain/repository/aggregate-saver.js +2 -0
  107. package/dist/cjs/domain/specification/specification.d.ts +9 -0
  108. package/dist/cjs/domain/specification/specification.js +44 -0
  109. package/dist/cjs/domain/type/abstract-type.d.ts +18 -0
  110. package/dist/cjs/domain/type/abstract-type.js +61 -0
  111. package/dist/cjs/domain/type/array-type.d.ts +17 -0
  112. package/dist/cjs/domain/type/array-type.js +64 -0
  113. package/dist/cjs/domain/type/boolean-type.d.ts +4 -0
  114. package/dist/cjs/domain/type/boolean-type.js +16 -0
  115. package/dist/cjs/domain/type/composite/composite-brand.d.ts +4 -0
  116. package/dist/cjs/domain/type/composite/composite-brand.js +15 -0
  117. package/dist/cjs/domain/type/composite/composite-field.d.ts +18 -0
  118. package/dist/cjs/domain/type/composite/composite-field.js +86 -0
  119. package/dist/cjs/domain/type/composite/composite-issues.d.ts +6 -0
  120. package/dist/cjs/domain/type/composite/composite-issues.js +35 -0
  121. package/dist/cjs/domain/type/composite/composite.d.ts +26 -0
  122. package/dist/cjs/domain/type/composite/composite.js +51 -0
  123. package/dist/cjs/domain/type/date-type.d.ts +19 -0
  124. package/dist/cjs/domain/type/date-type.js +70 -0
  125. package/dist/cjs/domain/type/enum-type.d.ts +7 -0
  126. package/dist/cjs/domain/type/enum-type.js +30 -0
  127. package/dist/cjs/domain/type/id-type.d.ts +7 -0
  128. package/dist/cjs/domain/type/id-type.js +17 -0
  129. package/dist/cjs/domain/type/index.d.ts +15 -0
  130. package/dist/cjs/domain/type/index.js +29 -0
  131. package/dist/cjs/domain/type/json-type.d.ts +9 -0
  132. package/dist/cjs/domain/type/json-type.js +46 -0
  133. package/dist/cjs/domain/type/number-type.d.ts +4 -0
  134. package/dist/cjs/domain/type/number-type.js +16 -0
  135. package/dist/cjs/domain/type/required/required-mark.d.ts +8 -0
  136. package/dist/cjs/domain/type/required/required-mark.js +13 -0
  137. package/dist/cjs/domain/type/required/required-type.d.ts +7 -0
  138. package/dist/cjs/domain/type/required/required-type.js +21 -0
  139. package/dist/cjs/domain/type/string-type.d.ts +4 -0
  140. package/dist/cjs/domain/type/string-type.js +16 -0
  141. package/dist/cjs/domain/type/uuid-type.d.ts +7 -0
  142. package/dist/cjs/domain/type/uuid-type.js +27 -0
  143. package/dist/cjs/infrastructure/clock/fixed-clock.d.ts +8 -0
  144. package/dist/cjs/infrastructure/clock/fixed-clock.js +19 -0
  145. package/dist/cjs/infrastructure/clock/system-clock.d.ts +4 -0
  146. package/dist/cjs/infrastructure/clock/system-clock.js +9 -0
  147. package/dist/cjs/infrastructure/event/domain-event-bus.d.ts +16 -0
  148. package/dist/cjs/infrastructure/event/domain-event-bus.js +60 -0
  149. package/dist/cjs/infrastructure/event/event-channel.d.ts +9 -0
  150. package/dist/cjs/infrastructure/event/event-channel.js +2 -0
  151. package/dist/cjs/infrastructure/event/event-registry.d.ts +9 -0
  152. package/dist/cjs/infrastructure/event/event-registry.js +43 -0
  153. package/dist/cjs/infrastructure/event/in-memory/emitter.d.ts +11 -0
  154. package/dist/cjs/infrastructure/event/in-memory/emitter.js +54 -0
  155. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.d.ts +16 -0
  156. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.js +50 -0
  157. package/dist/cjs/infrastructure/exceptions/event-delivery.exception.d.ts +5 -0
  158. package/dist/cjs/infrastructure/exceptions/event-delivery.exception.js +20 -0
  159. package/dist/cjs/infrastructure/exceptions/infrastructure-exception-code.d.ts +6 -0
  160. package/dist/cjs/infrastructure/exceptions/infrastructure-exception-code.js +10 -0
  161. package/dist/cjs/infrastructure/exceptions/infrastructure.exception.d.ts +6 -0
  162. package/dist/cjs/infrastructure/exceptions/infrastructure.exception.js +16 -0
  163. package/dist/cjs/infrastructure/exceptions/internal-error.exception.d.ts +5 -0
  164. package/dist/cjs/infrastructure/exceptions/internal-error.exception.js +17 -0
  165. package/dist/cjs/infrastructure/exceptions/kind/exception-classifier.d.ts +4 -0
  166. package/dist/cjs/infrastructure/exceptions/kind/exception-classifier.js +34 -0
  167. package/dist/cjs/infrastructure/exceptions/kind/exception-kind.d.ts +9 -0
  168. package/dist/cjs/infrastructure/exceptions/kind/exception-kind.js +13 -0
  169. package/dist/cjs/infrastructure/exceptions/unknown-event.exception.d.ts +4 -0
  170. package/dist/cjs/infrastructure/exceptions/unknown-event.exception.js +19 -0
  171. package/dist/cjs/infrastructure/index.d.ts +21 -0
  172. package/dist/cjs/infrastructure/index.js +33 -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/cjs/shared/exceptions/abstract.exception.d.ts +25 -0
  182. package/dist/cjs/shared/exceptions/abstract.exception.js +59 -0
  183. package/dist/cjs/shared/exceptions/brand/exception-brand.d.ts +6 -0
  184. package/dist/cjs/shared/exceptions/brand/exception-brand.js +29 -0
  185. package/dist/cjs/shared/exceptions/received/received-value.d.ts +4 -0
  186. package/dist/cjs/shared/exceptions/received/received-value.js +13 -0
  187. package/dist/cjs/shared/index.d.ts +8 -0
  188. package/dist/cjs/shared/index.js +13 -0
  189. package/dist/cjs/shared/issue-collection-error.d.ts +9 -0
  190. package/dist/cjs/shared/issue-collection-error.js +11 -0
  191. package/dist/cjs/shared/issue-collector.d.ts +8 -0
  192. package/dist/cjs/shared/issue-collector.js +41 -0
  193. package/dist/esm/application/event/event-bus.d.ts +4 -0
  194. package/dist/esm/application/event/event-bus.js +1 -0
  195. package/dist/esm/application/event/event-listener.d.ts +4 -0
  196. package/dist/esm/application/event/event-listener.js +2 -0
  197. package/dist/esm/application/event/listens-to.d.ts +15 -0
  198. package/dist/esm/application/event/listens-to.js +25 -0
  199. package/dist/esm/application/exceptions/aggregate-not-found.exception.d.ts +5 -0
  200. package/dist/esm/application/exceptions/aggregate-not-found.exception.js +13 -0
  201. package/dist/esm/application/exceptions/application-exception-code.d.ts +7 -0
  202. package/dist/esm/application/exceptions/application-exception-code.js +8 -0
  203. package/dist/esm/application/exceptions/application.exception.d.ts +6 -0
  204. package/dist/esm/application/exceptions/application.exception.js +12 -0
  205. package/dist/esm/application/exceptions/concurrency.exception.d.ts +5 -0
  206. package/dist/esm/application/exceptions/concurrency.exception.js +13 -0
  207. package/dist/esm/application/exceptions/not-allowed.exception.d.ts +5 -0
  208. package/dist/esm/application/exceptions/not-allowed.exception.js +13 -0
  209. package/dist/esm/application/exceptions/unexpected-error.exception.d.ts +5 -0
  210. package/dist/esm/application/exceptions/unexpected-error.exception.js +13 -0
  211. package/dist/esm/application/index.d.ts +14 -0
  212. package/dist/esm/application/index.js +10 -0
  213. package/dist/esm/application/ports/clock.d.ts +3 -0
  214. package/dist/esm/application/ports/clock.js +1 -0
  215. package/dist/esm/application/ports/logger.d.ts +8 -0
  216. package/dist/esm/application/ports/logger.js +1 -0
  217. package/dist/esm/application/use-case/use-case.d.ts +6 -0
  218. package/dist/esm/application/use-case/use-case.js +21 -0
  219. package/dist/esm/application/value-object-builder/value-object-builder.d.ts +16 -0
  220. package/dist/esm/application/value-object-builder/value-object-builder.js +47 -0
  221. package/dist/esm/domain/aggregate/aggregate-root.d.ts +9 -0
  222. package/dist/esm/domain/aggregate/aggregate-root.js +13 -0
  223. package/dist/esm/domain/entity/entity.d.ts +9 -0
  224. package/dist/esm/domain/entity/entity.js +15 -0
  225. package/dist/esm/domain/event/event-base.d.ts +23 -0
  226. package/dist/esm/domain/event/event-base.js +17 -0
  227. package/dist/esm/domain/exceptions/domain-exception-code.d.ts +7 -0
  228. package/dist/esm/domain/exceptions/domain-exception-code.js +8 -0
  229. package/dist/esm/domain/exceptions/domain.exception.d.ts +6 -0
  230. package/dist/esm/domain/exceptions/domain.exception.js +12 -0
  231. package/dist/esm/domain/exceptions/invalid-event-definition.exception.d.ts +4 -0
  232. package/dist/esm/domain/exceptions/invalid-event-definition.exception.js +15 -0
  233. package/dist/esm/domain/exceptions/required-value.exception.d.ts +4 -0
  234. package/dist/esm/domain/exceptions/required-value.exception.js +16 -0
  235. package/dist/esm/domain/exceptions/type-primitive.exception.d.ts +4 -0
  236. package/dist/esm/domain/exceptions/type-primitive.exception.js +16 -0
  237. package/dist/esm/domain/exceptions/validation.exception.d.ts +14 -0
  238. package/dist/esm/domain/exceptions/validation.exception.js +19 -0
  239. package/dist/esm/domain/index.d.ts +17 -0
  240. package/dist/esm/domain/index.js +12 -0
  241. package/dist/esm/domain/primitive/primitive-types.d.ts +20 -0
  242. package/dist/esm/domain/primitive/primitive-types.js +1 -0
  243. package/dist/esm/domain/repository/aggregate-finder.d.ts +4 -0
  244. package/dist/esm/domain/repository/aggregate-finder.js +1 -0
  245. package/dist/esm/domain/repository/aggregate-remover.d.ts +4 -0
  246. package/dist/esm/domain/repository/aggregate-remover.js +1 -0
  247. package/dist/esm/domain/repository/aggregate-saver.d.ts +4 -0
  248. package/dist/esm/domain/repository/aggregate-saver.js +1 -0
  249. package/dist/esm/domain/specification/specification.d.ts +9 -0
  250. package/dist/esm/domain/specification/specification.js +40 -0
  251. package/dist/esm/domain/type/abstract-type.d.ts +18 -0
  252. package/dist/esm/domain/type/abstract-type.js +57 -0
  253. package/dist/esm/domain/type/array-type.d.ts +17 -0
  254. package/dist/esm/domain/type/array-type.js +60 -0
  255. package/dist/esm/domain/type/boolean-type.d.ts +4 -0
  256. package/dist/esm/domain/type/boolean-type.js +12 -0
  257. package/dist/esm/domain/type/composite/composite-brand.d.ts +4 -0
  258. package/dist/esm/domain/type/composite/composite-brand.js +11 -0
  259. package/dist/esm/domain/type/composite/composite-field.d.ts +18 -0
  260. package/dist/esm/domain/type/composite/composite-field.js +82 -0
  261. package/dist/esm/domain/type/composite/composite-issues.d.ts +6 -0
  262. package/dist/esm/domain/type/composite/composite-issues.js +31 -0
  263. package/dist/esm/domain/type/composite/composite.d.ts +26 -0
  264. package/dist/esm/domain/type/composite/composite.js +48 -0
  265. package/dist/esm/domain/type/date-type.d.ts +19 -0
  266. package/dist/esm/domain/type/date-type.js +64 -0
  267. package/dist/esm/domain/type/enum-type.d.ts +7 -0
  268. package/dist/esm/domain/type/enum-type.js +26 -0
  269. package/dist/esm/domain/type/id-type.d.ts +7 -0
  270. package/dist/esm/domain/type/id-type.js +13 -0
  271. package/dist/esm/domain/type/index.d.ts +15 -0
  272. package/dist/esm/domain/type/index.js +12 -0
  273. package/dist/esm/domain/type/json-type.d.ts +9 -0
  274. package/dist/esm/domain/type/json-type.js +42 -0
  275. package/dist/esm/domain/type/number-type.d.ts +4 -0
  276. package/dist/esm/domain/type/number-type.js +12 -0
  277. package/dist/esm/domain/type/required/required-mark.d.ts +8 -0
  278. package/dist/esm/domain/type/required/required-mark.js +9 -0
  279. package/dist/esm/domain/type/required/required-type.d.ts +7 -0
  280. package/dist/esm/domain/type/required/required-type.js +18 -0
  281. package/dist/esm/domain/type/string-type.d.ts +4 -0
  282. package/dist/esm/domain/type/string-type.js +12 -0
  283. package/dist/esm/domain/type/uuid-type.d.ts +7 -0
  284. package/dist/esm/domain/type/uuid-type.js +23 -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 +46 -0
  299. package/dist/esm/infrastructure/exceptions/event-delivery.exception.d.ts +5 -0
  300. package/dist/esm/infrastructure/exceptions/event-delivery.exception.js +16 -0
  301. package/dist/esm/infrastructure/exceptions/infrastructure-exception-code.d.ts +6 -0
  302. package/dist/esm/infrastructure/exceptions/infrastructure-exception-code.js +7 -0
  303. package/dist/esm/infrastructure/exceptions/infrastructure.exception.d.ts +6 -0
  304. package/dist/esm/infrastructure/exceptions/infrastructure.exception.js +12 -0
  305. package/dist/esm/infrastructure/exceptions/internal-error.exception.d.ts +5 -0
  306. package/dist/esm/infrastructure/exceptions/internal-error.exception.js +13 -0
  307. package/dist/esm/infrastructure/exceptions/kind/exception-classifier.d.ts +4 -0
  308. package/dist/esm/infrastructure/exceptions/kind/exception-classifier.js +30 -0
  309. package/dist/esm/infrastructure/exceptions/kind/exception-kind.d.ts +9 -0
  310. package/dist/esm/infrastructure/exceptions/kind/exception-kind.js +10 -0
  311. package/dist/esm/infrastructure/exceptions/unknown-event.exception.d.ts +4 -0
  312. package/dist/esm/infrastructure/exceptions/unknown-event.exception.js +15 -0
  313. package/dist/esm/infrastructure/index.d.ts +21 -0
  314. package/dist/esm/infrastructure/index.js +15 -0
  315. package/dist/esm/infrastructure/logging/console-logger.d.ts +17 -0
  316. package/dist/esm/infrastructure/logging/console-logger.js +33 -0
  317. package/dist/esm/infrastructure/logging/in-memory-logger.d.ts +15 -0
  318. package/dist/esm/infrastructure/logging/in-memory-logger.js +23 -0
  319. package/dist/esm/infrastructure/logging/log-serializer.d.ts +7 -0
  320. package/dist/esm/infrastructure/logging/log-serializer.js +55 -0
  321. package/dist/esm/infrastructure/logging/silent-logger.d.ts +7 -0
  322. package/dist/esm/infrastructure/logging/silent-logger.js +14 -0
  323. package/dist/esm/package.json +3 -0
  324. package/dist/esm/shared/exceptions/abstract.exception.d.ts +25 -0
  325. package/dist/esm/shared/exceptions/abstract.exception.js +55 -0
  326. package/dist/esm/shared/exceptions/brand/exception-brand.d.ts +6 -0
  327. package/dist/esm/shared/exceptions/brand/exception-brand.js +25 -0
  328. package/dist/esm/shared/exceptions/received/received-value.d.ts +4 -0
  329. package/dist/esm/shared/exceptions/received/received-value.js +9 -0
  330. package/dist/esm/shared/index.d.ts +8 -0
  331. package/dist/esm/shared/index.js +5 -0
  332. package/dist/esm/shared/issue-collection-error.d.ts +9 -0
  333. package/dist/esm/shared/issue-collection-error.js +7 -0
  334. package/dist/esm/shared/issue-collector.d.ts +8 -0
  335. package/dist/esm/shared/issue-collector.js +37 -0
  336. package/package.json +95 -4
@@ -0,0 +1,419 @@
1
+ # Implementar una capa anticorrupción en TypeScript
2
+
3
+ ## Estructura
4
+
5
+ IDDD propone tres piezas [IDDD cap. 3 y cap. 13]:
6
+
7
+ 1. **Interfaz de la capa en el dominio:** un servicio de dominio con nombre del lenguaje local (en IDDD, `MemberService` que proporciona `ProductOwner` y `TeamMember`).
8
+ 2. **Adaptador:** cliente del sistema remoto; habla su protocolo (en IDDD, `IdentityAccessNotificationAdapter`).
9
+ 3. **Traductor:** convierte el Published Language remoto en objetos del modelo local (en IDDD, `MemberTranslator`).
10
+
11
+ El dominio solo ve la interfaz. Adaptador y traductor viven en infraestructura. Así el modelo usa datos de otro contexto para expresar sus propios conceptos sin contaminarse [IDDD cap. 3].
12
+
13
+ `@archi-code/ddd` no trae ninguna de las tres piezas: se escriben en el proyecto con sus value objects, sus excepciones y sus puertos `[Fuera de los libros: convención de @archi-code/ddd]`.
14
+
15
+ ```
16
+ src/contexts/sales/
17
+ domain/
18
+ catalog/ProductPricing.ts ← puerto con lenguaje de Ventas (SellableProduct, ProductPricing)
19
+ order/...
20
+ infrastructure/
21
+ catalog/
22
+ CatalogHttpProductPricing.ts ← adaptador: habla HTTP con Catálogo
23
+ CatalogTranslator.ts ← traductor: JSON de Catálogo → value objects de Ventas
24
+ CatalogProductV2.ts ← forma del Published Language de Catálogo que Ventas lee
25
+ ```
26
+
27
+ ## Ejemplo: Ventas consulta precios a Catálogo
28
+
29
+ Catálogo expone un OHS con fichas completas (fotos, descripciones, variantes, tarifas por canal). Ventas solo necesita saber si una referencia se puede vender y a qué precio en el canal web.
30
+
31
+ ### 1. Puerto en el dominio de Ventas
32
+
33
+ ```ts
34
+ // src/contexts/sales/domain/catalog/ProductPricing.ts
35
+ import { Composite, Required, StringType } from '@archi-code/ddd/domain';
36
+ import { Money } from '../shared/Money';
37
+ import { Sku } from '../order/OrderLine';
38
+
39
+ export class ProductName extends Required(StringType) {}
40
+
41
+ export class SellableProduct extends Composite({ sku: Sku, name: ProductName, unitPrice: Money }) {}
42
+
43
+ export interface ProductPricing {
44
+ priceForSale(sku: Sku): Promise<SellableProduct | null>;
45
+ }
46
+ ```
47
+
48
+ Fíjate en el nombre: `ProductPricing` y `SellableProduct`, no `CatalogClient` ni `CatalogProductDto`. El dominio pregunta con su lenguaje y recibe value objects ya válidos (`SellableProduct` es un `Composite` de `Sku`, `ProductName` y `Money`); `null` significa que la referencia no se puede vender ahora en el canal web.
49
+
50
+ ### 2. Forma del Published Language
51
+
52
+ ```ts
53
+ // src/contexts/sales/infrastructure/catalog/CatalogProductV2.ts
54
+ export interface CatalogProductV2 {
55
+ sku: string;
56
+ displayName: string;
57
+ lifecycle: 'DRAFT' | 'ACTIVE' | 'DISCONTINUED';
58
+ channelPrices: { channel: string; amountMinor: number; currency: string }[];
59
+ }
60
+ ```
61
+
62
+ Solo lo que Ventas lee; el resto de campos de Catálogo se ignora. El adaptador comprueba solo la forma; los valores los validan los value objects al construirse (`Sku`, `Money`). Si prefieres validar con JSON Schema, cualquier validador sirve en este punto `[Fuera de los libros: elección de librería]`.
63
+
64
+ ### 3. Traductor
65
+
66
+ ```ts
67
+ // src/contexts/sales/infrastructure/catalog/CatalogTranslator.ts
68
+ import { SellableProduct } from '../../domain/catalog/ProductPricing';
69
+ import type { CatalogProductV2 } from './CatalogProductV2';
70
+
71
+ export class CatalogTranslator {
72
+ static isProductV2(body: unknown): body is CatalogProductV2 {
73
+ const candidate = body as Partial<CatalogProductV2> | null;
74
+ return (
75
+ typeof candidate === 'object' &&
76
+ candidate !== null &&
77
+ typeof candidate.sku === 'string' &&
78
+ typeof candidate.displayName === 'string' &&
79
+ typeof candidate.lifecycle === 'string' &&
80
+ Array.isArray(candidate.channelPrices)
81
+ );
82
+ }
83
+
84
+ toSellableProduct(remote: CatalogProductV2): SellableProduct | null {
85
+ if (remote.lifecycle !== 'ACTIVE') {
86
+ return null;
87
+ }
88
+ const web = remote.channelPrices.find((price) => price.channel === 'WEB');
89
+ if (web === undefined) {
90
+ return null;
91
+ }
92
+ return SellableProduct.fromPrimitives({
93
+ sku: remote.sku,
94
+ name: remote.displayName,
95
+ unitPrice: { amount: web.amountMinor, currency: web.currency },
96
+ });
97
+ }
98
+ }
99
+ ```
100
+
101
+ La regla "solo se vende lo activo con precio web" es una decisión de traducción. Si se convierte en regla del negocio de Ventas con matices propios, muévela al dominio. `SellableProduct.fromPrimitives` construye cada value object y, si Catálogo envía un valor que el modelo de Ventas no acepta, lanza una `ValidationException` con todos los campos afectados.
102
+
103
+ ### 4. Adaptador
104
+
105
+ ```ts
106
+ // src/contexts/sales/infrastructure/catalog/CatalogHttpProductPricing.ts
107
+ import { InfrastructureException } from '@archi-code/ddd/infrastructure';
108
+ import type { ProductPricing, SellableProduct } from '../../domain/catalog/ProductPricing';
109
+ import type { Sku } from '../../domain/order/OrderLine';
110
+ import { CatalogTranslator } from './CatalogTranslator';
111
+
112
+ export class CatalogUnavailable extends InfrastructureException {
113
+ constructor(sku: string, options: { cause?: unknown } = {}) {
114
+ super(`Catalog did not answer for ${sku}`, ['CAT001'], options);
115
+ }
116
+ }
117
+
118
+ export class CatalogContractViolation extends InfrastructureException {
119
+ constructor(sku: string, options: { cause?: unknown } = {}) {
120
+ super(`Catalog answered ${sku} outside its published contract`, ['CAT002'], options);
121
+ }
122
+ }
123
+
124
+ export interface CatalogHttp {
125
+ get(
126
+ path: string,
127
+ options: { headers: Record<string, string>; signal: AbortSignal },
128
+ ): Promise<{ status: number; ok: boolean; json(): Promise<unknown> }>;
129
+ }
130
+
131
+ export class CatalogHttpProductPricing implements ProductPricing {
132
+ constructor(
133
+ private readonly http: CatalogHttp,
134
+ private readonly translator = new CatalogTranslator(),
135
+ ) {}
136
+
137
+ async priceForSale(sku: Sku): Promise<SellableProduct | null> {
138
+ let response: Awaited<ReturnType<CatalogHttp['get']>>;
139
+ try {
140
+ response = await this.http.get(`/products/${encodeURIComponent(sku.value)}`, {
141
+ headers: { Accept: 'application/vnd.catalog.product.v2+json' },
142
+ signal: AbortSignal.timeout(800),
143
+ });
144
+ } catch (error) {
145
+ throw new CatalogUnavailable(sku.value, { cause: error });
146
+ }
147
+ if (response.status === 404) {
148
+ return null;
149
+ }
150
+ if (!response.ok) {
151
+ throw new CatalogUnavailable(sku.value);
152
+ }
153
+ const body = await response.json();
154
+ if (!CatalogTranslator.isProductV2(body)) {
155
+ throw new CatalogContractViolation(sku.value);
156
+ }
157
+ try {
158
+ return this.translator.toSellableProduct(body);
159
+ } catch (error) {
160
+ throw new CatalogContractViolation(sku.value, { cause: error });
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ Decisiones visibles:
167
+ * Timeout explícito: la red falla y hay latencia [IDDD cap. 13].
168
+ * Validación del contrato al entrar: si Catálogo rompe el esquema, el error se detecta en el borde y no dentro del modelo. Un valor inválido de Catálogo no llega al usuario como `ValidationException` de Ventas: el adaptador la envuelve en `CatalogContractViolation` con la original como `cause`.
169
+ * Errores con nombre y código propios (`InfrastructureException` con `CAT001`, `CAT002`). `ExceptionClassifier.classify(error)` clasifica cualquier error de infraestructura como `ExceptionKind.Internal` (500 en HTTP); para avisar de que Catálogo no está disponible, el punto de entrada traduce su código (`CAT001` → 503) antes de mirar el tipo (con 5xx el cuerpo lleva un mensaje genérico, nunca el del error).
170
+
171
+ ### 5. Uso desde la capa de aplicación
172
+
173
+ ```ts
174
+ // src/contexts/sales/application/place-order/PlaceOrder.ts
175
+ import type { AggregateSaver } from '@archi-code/ddd/domain';
176
+ import { UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
177
+ import type { Clock, EventBus } from '@archi-code/ddd/application';
178
+ import type { ProductPricing } from '../../domain/catalog/ProductPricing';
179
+ import { CustomerId } from '../../domain/customer/CustomerId';
180
+ import { Order, OrderEvent, OrderId } from '../../domain/order/Order';
181
+ import { OrderLine, OrderLines, Quantity, Sku } from '../../domain/order/OrderLine';
182
+ import { ShippingAddress } from '../../domain/order/ShippingAddress';
183
+ import { ProductNotSellable } from '../../domain/order/ProductNotSellable';
184
+
185
+ interface PlaceOrderInput {
186
+ customerId: string;
187
+ items: { sku: string; quantity: number }[];
188
+ shippingAddress: { street: string; city: string; country: string };
189
+ }
190
+
191
+ interface PlaceOrderCommand {
192
+ orderId: OrderId;
193
+ customerId: CustomerId;
194
+ items: { sku: Sku; quantity: Quantity }[];
195
+ shippingAddress: ShippingAddress;
196
+ }
197
+
198
+ export class PlaceOrder extends UseCase<PlaceOrderInput, PlaceOrderCommand, string> {
199
+ constructor(
200
+ private readonly orders: AggregateSaver<Order>,
201
+ private readonly pricing: ProductPricing,
202
+ private readonly events: EventBus<OrderEvent>,
203
+ private readonly clock: Clock,
204
+ ) {
205
+ super();
206
+ }
207
+
208
+ protected toCommand(input: PlaceOrderInput): PlaceOrderCommand {
209
+ return ValueObjectBuilder.build({
210
+ orderId: () => OrderId.generate(),
211
+ customerId: () => new CustomerId(input.customerId),
212
+ items: () =>
213
+ ValueObjectBuilder.buildList(input.items, (item) =>
214
+ ValueObjectBuilder.build({
215
+ sku: () => new Sku(item.sku),
216
+ quantity: () => new Quantity(item.quantity),
217
+ }),
218
+ ),
219
+ shippingAddress: () => ShippingAddress.fromPrimitives(input.shippingAddress),
220
+ });
221
+ }
222
+
223
+ protected async handle(command: PlaceOrderCommand): Promise<string> {
224
+ const lines: OrderLine[] = [];
225
+ for (const item of command.items) {
226
+ const product = await this.pricing.priceForSale(item.sku);
227
+ if (product === null) {
228
+ throw new ProductNotSellable(item.sku.value);
229
+ }
230
+ lines.push(
231
+ new OrderLine({ sku: product.sku, quantity: item.quantity, unitPrice: product.unitPrice }),
232
+ );
233
+ }
234
+ const order = Order.place(
235
+ command.orderId,
236
+ command.customerId,
237
+ new OrderLines({ items: lines }),
238
+ command.shippingAddress,
239
+ this.clock,
240
+ );
241
+ await this.orders.save(order);
242
+ await this.events.publish(order.pullDomainEvents());
243
+ return order.id.value;
244
+ }
245
+ }
246
+ ```
247
+
248
+ El precio se copia en la línea del pedido: Ventas guarda el estado remoto **mínimo** que necesita y deja de depender de Catálogo para ese pedido [IDDD cap. 3]. La consulta a Catálogo va antes de crear y guardar el pedido; como la transacción solo existe dentro del `save` del repositorio, nunca queda una abierta mientras se espera a la red `[Fuera de los libros: convención de @archi-code/ddd]`. `ProductNotSellable` es una `DomainException` de Ventas con código propio (`ORD003`), que `ExceptionClassifier` clasifica como `ExceptionKind.Invalid` (422 en HTTP).
249
+
250
+ ## ACL con un webhook entrante
251
+
252
+ Mismo patrón con un producto externo que llama a este contexto: el punto de entrada es el adaptador, la forma del mensaje es el contrato y la traducción produce la entrada de un caso de uso en el lenguaje local. Vive en `interfaces/` (como cualquier controlador) y solo conoce casos de uso y `ExceptionClassifier` de `@archi-code/ddd/infrastructure` [IDDD cap. 4]. Traduce cada `ExceptionKind` a lo que entiende el emisor: lo que merece reintento (un conflicto, un error interno) responde 5xx para que la pasarela vuelva a enviarlo, y lo que no cambiaría al reintentar responde 4xx `[Fuera de los libros]`. El router ya ha comprobado la firma del emisor y la forma del cuerpo antes de llamarlo `[Fuera de los libros]`.
253
+
254
+ ```ts
255
+ // src/contexts/payments/interfaces/http/PaymentGatewayWebhook.ts
256
+ import { ExceptionClassifier, ExceptionKind } from '@archi-code/ddd/infrastructure';
257
+ import type { Logger } from '@archi-code/ddd/application';
258
+ import type { RecordPaymentAuthorization } from '../../application/record-payment-authorization/RecordPaymentAuthorization';
259
+ import type { RecordPaymentRejection } from '../../application/record-payment-rejection/RecordPaymentRejection';
260
+
261
+ export interface GatewayEvent {
262
+ type: string;
263
+ created: number;
264
+ amount: number;
265
+ currency: string;
266
+ failure_code?: string;
267
+ metadata: { paymentId: string };
268
+ }
269
+
270
+ function statusOf(kind: ExceptionKind): number {
271
+ switch (kind) {
272
+ case ExceptionKind.Conflict:
273
+ case ExceptionKind.Internal:
274
+ return 503;
275
+ case ExceptionKind.NotFound:
276
+ return 404;
277
+ default:
278
+ return 422;
279
+ }
280
+ }
281
+
282
+ export class PaymentGatewayWebhook {
283
+ constructor(
284
+ private readonly recordAuthorization: RecordPaymentAuthorization,
285
+ private readonly recordRejection: RecordPaymentRejection,
286
+ private readonly logger: Logger,
287
+ ) {}
288
+
289
+ async receive(event: GatewayEvent): Promise<{ status: number }> {
290
+ try {
291
+ switch (event.type) {
292
+ case 'charge.authorized':
293
+ await this.recordAuthorization.execute({
294
+ paymentId: event.metadata.paymentId,
295
+ amount: { amount: event.amount, currency: event.currency },
296
+ authorizedAt: new Date(event.created * 1000).toISOString(),
297
+ });
298
+ break;
299
+ case 'charge.failed':
300
+ await this.recordRejection.execute({
301
+ paymentId: event.metadata.paymentId,
302
+ reason: event.failure_code ?? 'unknown',
303
+ });
304
+ break;
305
+ }
306
+ return { status: 204 };
307
+ } catch (error) {
308
+ const kind = ExceptionClassifier.classify(error);
309
+ if (kind === ExceptionKind.Internal) {
310
+ this.logger.error('payment_webhook_failed', { type: event.type, error });
311
+ }
312
+ return { status: statusOf(kind) };
313
+ }
314
+ }
315
+ }
316
+ ```
317
+
318
+ `charge.authorized` es lenguaje de la pasarela; `RecordPaymentAuthorization` es lenguaje de Pagos. Cada tipo de evento que interesa va a un caso de uso distinto, que construye sus value objects en `toCommand` (`PaymentId`, `Money`) y registra un hecho: no lo rechaza y es idempotente, porque la pasarela reintenta. Los tipos que este contexto no necesita se ignoran con 204.
319
+
320
+ ## ACL con eventos de otro contexto
321
+
322
+ Si el upstream publica eventos (con el mismo paquete o no), el downstream los lee con su propia clase: un `EventBase` con el nombre publicado y solo los campos que usa, nunca la clase del otro contexto importada de su código `[Fuera de los libros: convención de @archi-code/ddd]`. La factoría de `EventRegistry` es el traductor del mensaje, y el listener convierte el evento en la entrada de un caso de uso:
323
+
324
+ ```ts
325
+ // src/contexts/sales/application/register-stock-reservation/StockReserved.ts
326
+ import { EventBase } from '@archi-code/ddd/domain';
327
+ import type { EventMetadata } from '@archi-code/ddd/domain';
328
+
329
+ export class StockReserved extends EventBase {
330
+ static readonly eventName = 'warehouse.stock.StockReserved';
331
+
332
+ constructor(
333
+ readonly reservationId: string,
334
+ readonly orderId: string,
335
+ metadata?: EventMetadata,
336
+ ) {
337
+ super(metadata);
338
+ }
339
+ }
340
+ ```
341
+
342
+ ```ts
343
+ // src/contexts/sales/application/register-stock-reservation/RegisterStockReservationOnStockReserved.ts
344
+ import { EventListener, ListensTo } from '@archi-code/ddd/application';
345
+ import { RegisterStockReservation } from './RegisterStockReservation';
346
+ import { StockReserved } from './StockReserved';
347
+
348
+ @ListensTo(StockReserved, { consumer: 'sales.register-stock-reservation-on-stock-reserved' })
349
+ export class RegisterStockReservationOnStockReserved extends EventListener<StockReserved> {
350
+ constructor(private readonly registerStockReservation: RegisterStockReservation) {
351
+ super();
352
+ }
353
+
354
+ async handle(event: StockReserved): Promise<void> {
355
+ await this.registerStockReservation.execute({
356
+ orderId: event.orderId,
357
+ reservationId: event.reservationId,
358
+ });
359
+ }
360
+ }
361
+ ```
362
+
363
+ ```ts
364
+ // src/contexts/sales/composition.ts (extracto)
365
+ import { DomainEventBus, EventRegistry } from '@archi-code/ddd/infrastructure';
366
+
367
+ const warehouseEvents = new EventRegistry<StockReserved>().register(
368
+ StockReserved,
369
+ (json) => new StockReserved(json.reservationId as string, json.orderId as string, json),
370
+ );
371
+ const warehouseBus = new DomainEventBus<StockReserved>(
372
+ new RabbitMqEventChannel(amqpChannel, 'warehouse.published'),
373
+ warehouseEvents,
374
+ );
375
+ await warehouseBus.subscribe(
376
+ new RegisterStockReservationOnStockReserved(
377
+ new RegisterStockReservation(orders, salesBus, clock),
378
+ ),
379
+ );
380
+ ```
381
+
382
+ `RabbitMqEventChannel` es el canal de broker del proyecto (el de `ddd-skills/archi-code-ddd/patterns.md`), aquí suscrito al exchange donde Almacén publica su Published Language. La factoría pasa `json` como metadatos para conservar el `eventId` con el que se deduplica. Leer `json.orderId as string` no valida nada: el caso de uso construye `OrderId` y `ReservationId` en `toCommand`, y si el mensaje no cumple el contrato lanza una `ValidationException` con cada campo, el listener falla y el canal lo aparta (en RabbitMQ, `nack` a la cola de mensajes muertos). El caso de uso `RegisterStockReservation` y su idempotencia están en `ddd-skills/ddd-context-mapping/references/estilos-integracion.md`.
383
+
384
+ ## Tests de la capa
385
+
386
+ `[Fuera de los libros]`
387
+ * **Traductor:** tests unitarios con respuestas reales anonimizadas del sistema remoto.
388
+ * **Adaptador:** tests de contrato contra el esquema publicado o un entorno de pruebas del upstream.
389
+ * **Dominio y casos de uso:** usan una implementación en memoria del puerto; nunca se prueban contra el sistema remoto:
390
+
391
+ ```ts
392
+ import type { ProductPricing } from '../../src/contexts/sales/domain/catalog/ProductPricing';
393
+ import { SellableProduct } from '../../src/contexts/sales/domain/catalog/ProductPricing';
394
+ import type { Sku } from '../../src/contexts/sales/domain/order/OrderLine';
395
+
396
+ export class InMemoryProductPricing implements ProductPricing {
397
+ private readonly products = new Map<string, SellableProduct>();
398
+
399
+ add(product: SellableProduct): this {
400
+ this.products.set(product.sku.value, product);
401
+ return this;
402
+ }
403
+
404
+ async priceForSale(sku: Sku): Promise<SellableProduct | null> {
405
+ return this.products.get(sku.value) ?? null;
406
+ }
407
+ }
408
+ ```
409
+
410
+ Con ella, `PlaceOrder` se prueba con `new InMemoryOrderRepository()` (el repositorio en memoria que el proyecto escribe en `test/support/`, como en `ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md`), un `DomainEventBus` sobre `InMemoryEventChannel` y `new FixedClock('2026-03-01T09:30:00Z')`, comprobando `channel.sent` y lo guardado.
411
+
412
+ ## Errores frecuentes
413
+
414
+ * El puerto devuelve el DTO remoto "por comodidad" en vez de value objects. La capa ya no protege nada.
415
+ * El traductor vive en el dominio porque "es lógica". Si conoce el formato remoto, es infraestructura.
416
+ * Una única ACL genérica para todos los sistemas externos. Cada relación tiene su traducción.
417
+ * Copiar todos los campos remotos "por si acaso" en vez del estado mínimo.
418
+ * Dejar que una `ValidationException` provocada por datos del sistema remoto salga como si fuera un error del usuario (422): el adaptador la envuelve en su propia `InfrastructureException`.
419
+ * Importar la clase de evento del otro contexto para el `@ListensTo`: el downstream declara la suya, con el nombre publicado.
@@ -0,0 +1,152 @@
1
+ # Estilos de integración
2
+
3
+ ## Comparativa
4
+
5
+ | Estilo | Acoplamiento temporal | Robustez | Cuándo |
6
+ |---|---|---|---|
7
+ | RPC (SOAP, gRPC, HTTP con procedimientos) | Alto: si el otro cae, fallas | Baja. Parece una llamada local y no lo es [Destilado cap. 4] | Consultas rápidas a servicios muy disponibles; siempre con ACL |
8
+ | REST | Alto en peticiones directas | Media. Se apoya en la fiabilidad probada de la web [Destilado cap. 4] | OHS para varios consumidores; consultas de detalle (query-back) |
9
+ | Mensajería con eventos | Bajo | La más robusta: elimina gran parte del acoplamiento temporal y obliga a diseñar contando con latencia [Destilado cap. 4] | Notificar hechos, coordinar procesos entre contextos |
10
+ | Base de datos o ficheros compartidos | Estructural | Pésima. Evitar [Destilado cap. 4; IDDD cap. 13] | Solo heredados sin alternativa, con ACL |
11
+
12
+ ## Principios de la computación distribuida
13
+
14
+ IDDD los formula como principios, no como falacias, para subrayar que hay que diseñar contando con ellos [IDDD cap. 13]: la red no es fiable; siempre hay latencia; el ancho de banda no es infinito; la red no es segura; la topología cambia; el conocimiento y las políticas están repartidos entre varios administradores; el transporte tiene coste; la red es heterogénea.
15
+
16
+ ## REST
17
+
18
+ * El contexto que ofrece REST debe proporcionar un OHS con Published Language; los recursos son el idioma publicado [Destilado cap. 4].
19
+ * Recursos **sintéticos**: con la forma que necesita el caso de uso del cliente, no la del agregado. Reflejar los agregados fuerza a todos los clientes a ser Conformist ante cualquier cambio del modelo [Destilado cap. 4].
20
+ * Para reducir la dependencia temporal aunque solo haya REST: llama al sistema remoto desde un temporizador o al recibir un mensaje propio, con reintentos y espera creciente, en lugar de hacerlo dentro de la petición del usuario [IDDD cap. 13].
21
+
22
+ ### REST asíncrono (notification log)
23
+
24
+ Publicar los eventos de dominio como un feed de recursos (al estilo Atom) que los clientes sondean periódicamente. Si el servicio no está disponible, el cliente reintenta más tarde [Destilado cap. 4]. IDDD lo implementa con un log de notificaciones actual y logs archivados navegables; cada cliente es responsable de no consumir duplicados [IDDD cap. 3 y cap. 13].
25
+
26
+ ## Mensajería
27
+
28
+ * El contexto suscrito reacciona a eventos de dominio publicados por otros, y eso suele crear o modificar un agregado propio [Destilado cap. 4].
29
+ * Si el cliente necesita forzar una acción, envía un **mensaje de comando** y recibe el resultado como un evento publicado [Destilado cap. 4].
30
+ * **Entrega al menos una vez** en el mecanismo y **receptor idempotente** en el consumidor: deduplicar o reaplicar con el mismo resultado [Destilado cap. 4].
31
+ * Las peticiones no deben bloquearse esperando respuesta; siempre habrá algo de latencia y hay que planificarla [Destilado cap. 4].
32
+
33
+ ### Publicar de forma fiable
34
+
35
+ `[Fuera de los libros como nombre; la idea de guardar eventos en la misma transacción está en IDDD cap. 8]`. Patrón **outbox transaccional**: en la misma transacción que guarda el agregado, se guardan sus eventos en una tabla; un proceso aparte los lee y los publica en el bus, marcándolos como enviados. Evita perder eventos si el bus falla tras confirmar la transacción, o publicar eventos de cambios que luego se revierten. Se desarrolla en `ddd-skills/ddd-eventos-dominio/GUIA.md`. Con `@archi-code/ddd` la aplicación no abre transacciones: el caso de uso sigue llamando a `EventBus.publish` después de `save`, y el outbox lo escribe el adaptador del repositorio, que guarda el agregado y sus eventos pendientes en la misma escritura atómica de `save` (el mismo documento o la misma transacción de base de datos); un reenviador los entrega después por un `EventChannel` `[Fuera de los libros: convención de @archi-code/ddd]`.
36
+
37
+ ### Receptor idempotente en TypeScript
38
+
39
+ El evento de Almacén llega por un `EventChannel` a un listener (`RegisterStockReservationOnStockReserved`, en `ddd-skills/ddd-context-mapping/references/anticorrupcion-ts.md`), que ejecuta un solo caso de uso. Ese caso de uso registra un hecho: no lo rechaza, vuelve sin error si el pedido no existe (un error haría que el canal lo reentregara sin fin) y el método del agregado es idempotente:
40
+
41
+ ```ts
42
+ // src/contexts/sales/application/register-stock-reservation/RegisterStockReservation.ts
43
+ import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
44
+ import { UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
45
+ import type { Clock, EventBus } from '@archi-code/ddd/application';
46
+ import { Order, OrderEvent, OrderId, ReservationId } from '../../domain/order/Order';
47
+
48
+ interface RegisterStockReservationInput {
49
+ orderId: string;
50
+ reservationId: string;
51
+ }
52
+
53
+ interface RegisterStockReservationCommand {
54
+ orderId: OrderId;
55
+ reservationId: ReservationId;
56
+ }
57
+
58
+ export class RegisterStockReservation extends UseCase<
59
+ RegisterStockReservationInput,
60
+ RegisterStockReservationCommand,
61
+ void
62
+ > {
63
+ constructor(
64
+ private readonly orders: AggregateFinder<Order> & AggregateSaver<Order>,
65
+ private readonly events: EventBus<OrderEvent>,
66
+ private readonly clock: Clock,
67
+ ) {
68
+ super();
69
+ }
70
+
71
+ protected toCommand(input: RegisterStockReservationInput): RegisterStockReservationCommand {
72
+ return ValueObjectBuilder.build({
73
+ orderId: () => new OrderId(input.orderId),
74
+ reservationId: () => new ReservationId(input.reservationId),
75
+ });
76
+ }
77
+
78
+ protected async handle(command: RegisterStockReservationCommand): Promise<void> {
79
+ const order = await this.orders.findById(command.orderId);
80
+ if (order === null) {
81
+ return;
82
+ }
83
+ order.registerStockReservation(command.reservationId, this.clock);
84
+ await this.orders.save(order);
85
+ await this.events.publish(order.pullDomainEvents());
86
+ }
87
+ }
88
+ ```
89
+
90
+ El método de `Order` (el de `ddd-skills/ddd-agregados/references/implementacion-ts.md`), que guarda la reserva en `private _stockReservation: ReservationId | null` (`null` porque al principio no hay reserva) y confirma el pedido cuando también tiene la autorización de pago:
91
+
92
+ ```ts
93
+ registerStockReservation(reservation: ReservationId, clock: { now(): Date }): void {
94
+ if (!this._status.isPending()) {
95
+ return;
96
+ }
97
+ this._stockReservation = reservation;
98
+ this.tryToConfirm(clock);
99
+ }
100
+ ```
101
+
102
+ Aplicar dos veces "reserva registrada" no cambia nada la segunda vez ni graba un segundo evento. Cuando la operación no es idempotente por naturaleza, el `eventId` del mensaje se guarda como procesado en la misma escritura atómica que el cambio, y si ya estaba no se hace nada `[Fuera de los libros]`; con el paquete esa escritura la hace el adaptador que persiste el cambio, no el caso de uso, que solo le pasa el `eventId` (ver "Consumidor idempotente" en `ddd-skills/ddd-eventos-dominio/references/publicacion-ts.md`) `[Fuera de los libros: convención de @archi-code/ddd]`. Si dos mensajes del mismo pedido llegan a la vez, `save` lanza `ConcurrencyException` en uno de ellos y el listener falla; lo que pase después lo decide el canal (reintento o cola de mensajes muertos), y en el reintento el caso de uso vuelve a cargar el pedido y la operación idempotente no cambia nada.
103
+
104
+ ## Contratos y versionado
105
+
106
+ * Dos opciones para intercambiar estructuras: distribuir clases e interfaces a todos los sistemas (tipado fuerte, pero recompilaciones y riesgo de usar objetos ajenos como propios) o un contrato basado en estándar, un media type propio que es un Published Language [IDDD cap. 13].
107
+ * Con contrato, el consumidor solo lee datos; no tiene acceso a comportamiento del otro modelo, y IDDD lo ve como protección, no como carencia. Los cálculos los hace el productor y los envía como atributos del evento [IDDD cap. 13].
108
+ * Los eventos y notificaciones llevan versión. Si se diseñan con cuidado, muchos consumidores pueden leer cualquier versión como si fuera la 1 y no necesitan cambiar [IDDD cap. 13].
109
+ * Los value objects estables (identificadores) se pueden incluir en el evento; quedan congelados en la estructura [IDDD cap. 13]. Con el paquete, el payload lleva solo primitivos tomados de los value objects (`orderId: string`), también los identificadores `[Fuera de los libros: convención de @archi-code/ddd]`.
110
+
111
+ La notificación de IDDD envuelve el evento en `{ notificationId, typeName, version, occurredOn, event }` [IDDD cap. 13]. Con el paquete, el mensaje publicado es un `SerializedEvent` plano, con el nombre del Published Language, que también identifica su versión `[Fuera de los libros: convención de @archi-code/ddd]`:
112
+
113
+ ```json
114
+ {
115
+ "eventName": "sales.order.OrderPlaced",
116
+ "eventId": "6f1c2a3b-8d4e-4f5a-9b6c-7d8e9f0a1b2c",
117
+ "occurredOn": "2026-10-18T10:15:00.000Z",
118
+ "orderId": "9b2f6c1e-4d3a-4f8b-a1c2-3e4d5f6a7b8c",
119
+ "customerId": "1c5e7a90-2b3d-4e6f-8a1b-2c3d4e5f6a7b",
120
+ "lines": [
121
+ { "sku": "TORCH-01", "quantity": 2, "unitPrice": { "amount": 850, "currency": "EUR" } }
122
+ ],
123
+ "total": { "amount": 1700, "currency": "EUR" }
124
+ }
125
+ ```
126
+
127
+ `eventName` hace de `typeName` y de `version`: el mensaje no lleva campo de versión, y una versión incompatible del contrato se publica con otro nombre (`sales.order.OrderPlacedV2`) mientras los consumidores migran. `eventId` es el identificador con el que el consumidor deduplica. No hay `notificationId` secuencial: si publicas un feed REST de notificaciones, la secuencia la asigna tu canal duradero (la fila del outbox).
128
+
129
+ El evento interno no sale tal cual. La traducción al Published Language vive en infraestructura, en el adaptador que habla con el broker: un `EventChannel` del proyecto (`ContractPublishingChannel`) que pasa cada `SerializedEvent` por `SalesContracts` (qué eventos salen y con qué contrato, uno por versión publicada, con `messageId` = `eventId`) y entrega todos al canal interno. El código está en `ddd-skills/ddd-eventos-dominio/references/publicacion-ts.md` ("Lenguaje publicado en el adaptador del broker"); los casos de uso siguen viendo solo `EventBus`, y un evento sin contrato es interno y no sale del contexto. Para no perder mensajes, la publicación al broker la hace el reenviador del outbox después del commit (ver "Publicar de forma fiable").
130
+
131
+ El consumidor no valida el mensaje con un esquema aparte: lo lee con su propia clase `extends EventBase` (solo los campos que usa) y el caso de uso que ejecuta su listener construye cada valor como value object en `toCommand`, que lanza una `ValidationException` con todos los campos que no cumplen el contrato. El ejemplo completo está en `ddd-skills/ddd-context-mapping/references/anticorrupcion-ts.md`.
132
+
133
+ ## Evento enriquecido o consulta al origen
134
+
135
+ | | Evento enriquecido | Evento ligero + query-back |
136
+ |---|---|---|
137
+ | Autonomía del consumidor | Alta | Depende de que el origen responda |
138
+ | Previsión de necesidades | Difícil acertar con todo | No hace falta |
139
+ | Seguridad | Riesgo de exponer datos de más | El origen controla el acceso en cada consulta |
140
+
141
+ Hay casos que piden una mezcla [Destilado cap. 4]. En el ejemplo del libro, `PolicyIssued` lleva el identificador de la póliza; el consumidor lo guarda como `issuedPolicyId` para trazabilidad y consulta al origen con `GET` si necesita más [Destilado cap. 4].
142
+
143
+ Pauta `[Fuera de los libros]`: incluye en el evento lo que el consumidor necesita para decidir y que no cambiará (identificadores, importes cerrados, fechas del hecho); deja fuera datos personales que el consumidor no necesite (minimización de datos) y datos que cambian a menudo.
144
+
145
+ ## Procesos de larga duración
146
+
147
+ Cuando un proceso implica a varios contextos, se coordina con eventos y un objeto que guarda el estado del proceso, con un rastreador de tiempos para detectar respuestas que no llegan (estados como no completado, completado normalmente o agotado por tiempo) [IDDD cap. 13]. En la práctica actual se llama a esto process manager o saga `[Fuera de los libros como nombres]`. Reglas:
148
+
149
+ * El estado del proceso es un concepto del dominio con nombre del lenguaje (`ConfirmaciónDePedido`), no un `SagaState` genérico.
150
+ * Cada paso tiene un resultado esperado y un plazo.
151
+ * Las acciones compensatorias (liberar reserva, anular autorización) son comandos de negocio, no "rollbacks".
152
+ * Cuando un recurso remoto aún no existe, modela esa situación como estado. En IDDD, una discusión remota tiene estados de disponibilidad: complemento no contratado, no solicitada, solicitada y lista [IDDD cap. 3].