@archi-code/ddd 3.0.0-alpha.1 → 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 (282) hide show
  1. package/README.md +478 -376
  2. package/SKILL.md +10 -8
  3. package/ddd-skills/archi-code-ddd/imports.md +28 -18
  4. package/ddd-skills/archi-code-ddd/layers.md +68 -53
  5. package/ddd-skills/archi-code-ddd/patterns.md +50 -43
  6. package/ddd-skills/archi-code-ddd/what-not-to-do.md +25 -18
  7. package/ddd-skills/archi-code-ddd/where-each-class-goes.md +11 -10
  8. package/ddd-skills/ddd-agregados/GUIA.md +2 -2
  9. package/ddd-skills/ddd-agregados/references/consistencia-eventual.md +5 -6
  10. package/ddd-skills/ddd-agregados/references/fabricas.md +5 -8
  11. package/ddd-skills/ddd-agregados/references/implementacion-ts.md +22 -27
  12. package/ddd-skills/ddd-arquitectura/GUIA.md +4 -4
  13. package/ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs +13 -6
  14. package/ddd-skills/ddd-arquitectura/references/estilos-adicionales.md +2 -2
  15. package/ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md +70 -16
  16. package/ddd-skills/ddd-auditoria-refactorizacion/GUIA.md +1 -1
  17. package/ddd-skills/ddd-auditoria-refactorizacion/references/ejemplo-auditoria.md +9 -11
  18. package/ddd-skills/ddd-auditoria-refactorizacion/references/lista-comprobacion.md +4 -4
  19. package/ddd-skills/ddd-auditoria-refactorizacion/references/plan-migracion.md +2 -2
  20. package/ddd-skills/ddd-context-mapping/GUIA.md +5 -5
  21. package/ddd-skills/ddd-context-mapping/references/anticorrupcion-ts.md +33 -18
  22. package/ddd-skills/ddd-context-mapping/references/estilos-integracion.md +4 -7
  23. package/ddd-skills/ddd-event-sourcing-cqrs/GUIA.md +2 -2
  24. package/ddd-skills/ddd-event-sourcing-cqrs/references/event-sourcing-ts.md +32 -27
  25. package/ddd-skills/ddd-event-sourcing-cqrs/references/proyecciones-cqrs-ts.md +7 -7
  26. package/ddd-skills/ddd-eventos-dominio/GUIA.md +3 -3
  27. package/ddd-skills/ddd-eventos-dominio/references/diseno-eventos.md +15 -16
  28. package/ddd-skills/ddd-eventos-dominio/references/ejemplo-ecommerce.md +11 -11
  29. package/ddd-skills/ddd-eventos-dominio/references/publicacion-ts.md +34 -31
  30. package/ddd-skills/ddd-repositorios-persistencia/GUIA.md +7 -7
  31. package/ddd-skills/ddd-repositorios-persistencia/references/implementacion-ts.md +50 -32
  32. package/ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md +31 -17
  33. package/ddd-skills/ddd-repositorios-persistencia/references/transacciones-ts.md +21 -14
  34. package/ddd-skills/ddd-servicios-capa-aplicacion/GUIA.md +2 -2
  35. package/ddd-skills/ddd-servicios-capa-aplicacion/references/capa-aplicacion-ts.md +19 -19
  36. package/ddd-skills/ddd-servicios-capa-aplicacion/references/ejemplo-ecommerce.md +12 -15
  37. package/ddd-skills/ddd-servicios-capa-aplicacion/references/servicios-dominio-ts.md +5 -6
  38. package/ddd-skills/ddd-value-objects-entidades/GUIA.md +3 -3
  39. package/ddd-skills/ddd-value-objects-entidades/references/entidades-ts.md +7 -9
  40. package/ddd-skills/ddd-value-objects-entidades/references/persistencia-valores.md +5 -5
  41. package/ddd-skills/ddd-value-objects-entidades/references/validacion.md +9 -9
  42. package/ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md +2 -3
  43. package/dist/cjs/application/event/listens-to.js +4 -4
  44. package/dist/cjs/{exceptions/application → application/exceptions}/aggregate-not-found.exception.d.ts +1 -1
  45. package/dist/cjs/{exceptions/application → application/exceptions}/aggregate-not-found.exception.js +4 -4
  46. package/dist/cjs/application/exceptions/application-exception-code.d.ts +7 -0
  47. package/dist/cjs/application/exceptions/application-exception-code.js +11 -0
  48. package/dist/cjs/application/exceptions/application.exception.d.ts +6 -0
  49. package/dist/cjs/application/exceptions/application.exception.js +16 -0
  50. package/dist/cjs/{exceptions/application → application/exceptions}/concurrency.exception.d.ts +1 -1
  51. package/dist/cjs/{exceptions/application → application/exceptions}/concurrency.exception.js +4 -4
  52. package/dist/cjs/{exceptions/application → application/exceptions}/not-allowed.exception.d.ts +1 -1
  53. package/dist/cjs/{exceptions/application → application/exceptions}/not-allowed.exception.js +4 -4
  54. package/dist/cjs/application/exceptions/unexpected-error.exception.d.ts +5 -0
  55. package/dist/cjs/application/exceptions/unexpected-error.exception.js +17 -0
  56. package/dist/cjs/application/index.d.ts +7 -0
  57. package/dist/cjs/application/index.js +15 -1
  58. package/dist/cjs/application/use-case/use-case.js +3 -2
  59. package/dist/{esm/domain/type/value-objects/value-objects.d.ts → cjs/application/value-object-builder/value-object-builder.d.ts} +3 -3
  60. package/dist/cjs/application/value-object-builder/value-object-builder.js +51 -0
  61. package/dist/cjs/domain/aggregate/aggregate-root.d.ts +0 -5
  62. package/dist/cjs/domain/aggregate/aggregate-root.js +4 -20
  63. package/dist/cjs/domain/entity/entity.js +2 -2
  64. package/dist/cjs/domain/event/event-base.d.ts +3 -9
  65. package/dist/cjs/domain/event/event-base.js +8 -38
  66. package/dist/cjs/domain/exceptions/domain-exception-code.d.ts +7 -0
  67. package/dist/cjs/domain/exceptions/domain-exception-code.js +11 -0
  68. package/dist/cjs/domain/exceptions/domain.exception.d.ts +6 -0
  69. package/dist/cjs/domain/exceptions/domain.exception.js +16 -0
  70. package/dist/cjs/domain/exceptions/invalid-event-definition.exception.d.ts +4 -0
  71. package/dist/cjs/domain/exceptions/invalid-event-definition.exception.js +19 -0
  72. package/dist/cjs/{exceptions/domain → domain/exceptions}/required-value.exception.js +5 -6
  73. package/dist/cjs/{exceptions/domain → domain/exceptions}/type-primitive.exception.js +5 -6
  74. package/dist/cjs/{exceptions/domain → domain/exceptions}/validation.exception.js +5 -5
  75. package/dist/cjs/domain/index.d.ts +8 -1
  76. package/dist/cjs/domain/index.js +14 -2
  77. package/dist/cjs/domain/primitive/primitive-types.d.ts +1 -3
  78. package/dist/cjs/domain/type/abstract-type.d.ts +6 -8
  79. package/dist/cjs/domain/type/abstract-type.js +15 -23
  80. package/dist/cjs/domain/type/array-type.d.ts +2 -1
  81. package/dist/cjs/domain/type/array-type.js +17 -18
  82. package/dist/cjs/domain/type/boolean-type.js +2 -2
  83. package/dist/cjs/domain/type/composite/composite-brand.d.ts +4 -0
  84. package/dist/cjs/domain/type/composite/composite-brand.js +15 -0
  85. package/dist/cjs/domain/type/composite/composite-field.d.ts +7 -8
  86. package/dist/cjs/domain/type/composite/composite-field.js +70 -47
  87. package/dist/cjs/domain/type/composite/composite-issues.d.ts +6 -0
  88. package/dist/cjs/domain/type/composite/composite-issues.js +35 -0
  89. package/dist/cjs/domain/type/composite/composite.js +15 -19
  90. package/dist/cjs/domain/type/date-type.d.ts +3 -2
  91. package/dist/cjs/domain/type/date-type.js +42 -7
  92. package/dist/cjs/domain/type/enum-type.js +2 -2
  93. package/dist/cjs/domain/type/id-type.d.ts +1 -1
  94. package/dist/cjs/domain/type/id-type.js +4 -4
  95. package/dist/cjs/domain/type/index.d.ts +2 -4
  96. package/dist/cjs/domain/type/index.js +3 -7
  97. package/dist/cjs/domain/type/json-type.d.ts +4 -0
  98. package/dist/cjs/domain/type/json-type.js +33 -4
  99. package/dist/cjs/domain/type/number-type.js +2 -2
  100. package/dist/cjs/domain/type/required/required-mark.d.ts +0 -1
  101. package/dist/cjs/domain/type/string-type.js +2 -2
  102. package/dist/cjs/domain/type/uuid-type.js +2 -2
  103. package/dist/cjs/infrastructure/event/domain-event-bus.js +9 -9
  104. package/dist/cjs/infrastructure/event/event-registry.js +9 -9
  105. package/dist/cjs/infrastructure/event/in-memory/emitter.js +2 -2
  106. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.d.ts +1 -1
  107. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.js +6 -7
  108. package/dist/cjs/{exceptions/infrastructure → infrastructure/exceptions}/event-delivery.exception.js +4 -4
  109. package/dist/cjs/infrastructure/exceptions/infrastructure-exception-code.d.ts +6 -0
  110. package/dist/cjs/infrastructure/exceptions/infrastructure-exception-code.js +10 -0
  111. package/dist/cjs/infrastructure/exceptions/infrastructure.exception.d.ts +6 -0
  112. package/dist/cjs/infrastructure/exceptions/infrastructure.exception.js +16 -0
  113. package/dist/cjs/{exceptions/infrastructure → infrastructure/exceptions}/internal-error.exception.d.ts +1 -1
  114. package/dist/cjs/{exceptions/infrastructure → infrastructure/exceptions}/internal-error.exception.js +4 -4
  115. package/dist/cjs/infrastructure/exceptions/kind/exception-classifier.d.ts +4 -0
  116. package/dist/cjs/infrastructure/exceptions/kind/exception-classifier.js +34 -0
  117. package/dist/cjs/infrastructure/exceptions/kind/exception-kind.d.ts +9 -0
  118. package/dist/cjs/infrastructure/exceptions/kind/exception-kind.js +13 -0
  119. package/dist/cjs/{exceptions/infrastructure → infrastructure/exceptions}/unknown-event.exception.js +4 -4
  120. package/dist/cjs/infrastructure/index.d.ts +7 -2
  121. package/dist/cjs/infrastructure/index.js +15 -3
  122. package/dist/cjs/infrastructure/logging/console-logger.js +2 -2
  123. package/dist/{esm → cjs/shared}/exceptions/abstract.exception.d.ts +6 -4
  124. package/dist/cjs/{exceptions → shared/exceptions}/abstract.exception.js +9 -16
  125. package/dist/cjs/shared/index.d.ts +8 -0
  126. package/dist/cjs/shared/index.js +13 -0
  127. package/dist/cjs/shared/issue-collection-error.d.ts +9 -0
  128. package/dist/cjs/shared/issue-collection-error.js +11 -0
  129. package/dist/cjs/shared/issue-collector.d.ts +8 -0
  130. package/dist/cjs/shared/issue-collector.js +41 -0
  131. package/dist/esm/application/event/listens-to.js +1 -1
  132. package/dist/esm/{exceptions/application → application/exceptions}/aggregate-not-found.exception.d.ts +1 -1
  133. package/dist/esm/application/exceptions/aggregate-not-found.exception.js +13 -0
  134. package/dist/esm/application/exceptions/application-exception-code.d.ts +7 -0
  135. package/dist/esm/application/exceptions/application-exception-code.js +8 -0
  136. package/dist/esm/application/exceptions/application.exception.d.ts +6 -0
  137. package/dist/esm/application/exceptions/application.exception.js +12 -0
  138. package/dist/esm/{exceptions/application → application/exceptions}/concurrency.exception.d.ts +1 -1
  139. package/dist/esm/{exceptions/application → application/exceptions}/concurrency.exception.js +4 -4
  140. package/dist/esm/{exceptions/application → application/exceptions}/not-allowed.exception.d.ts +1 -1
  141. package/dist/esm/application/exceptions/not-allowed.exception.js +13 -0
  142. package/dist/esm/application/exceptions/unexpected-error.exception.d.ts +5 -0
  143. package/dist/esm/application/exceptions/unexpected-error.exception.js +13 -0
  144. package/dist/esm/application/index.d.ts +7 -0
  145. package/dist/esm/application/index.js +7 -0
  146. package/dist/esm/application/use-case/use-case.js +3 -2
  147. package/dist/{cjs/domain/type/value-objects/value-objects.d.ts → esm/application/value-object-builder/value-object-builder.d.ts} +3 -3
  148. package/dist/esm/application/value-object-builder/value-object-builder.js +47 -0
  149. package/dist/esm/domain/aggregate/aggregate-root.d.ts +0 -5
  150. package/dist/esm/domain/aggregate/aggregate-root.js +4 -20
  151. package/dist/esm/domain/entity/entity.js +1 -1
  152. package/dist/esm/domain/event/event-base.d.ts +3 -9
  153. package/dist/esm/domain/event/event-base.js +8 -38
  154. package/dist/esm/domain/exceptions/domain-exception-code.d.ts +7 -0
  155. package/dist/esm/domain/exceptions/domain-exception-code.js +8 -0
  156. package/dist/esm/domain/exceptions/domain.exception.d.ts +6 -0
  157. package/dist/esm/domain/exceptions/domain.exception.js +12 -0
  158. package/dist/esm/domain/exceptions/invalid-event-definition.exception.d.ts +4 -0
  159. package/dist/esm/domain/exceptions/invalid-event-definition.exception.js +15 -0
  160. package/dist/esm/{exceptions/domain → domain/exceptions}/required-value.exception.js +4 -5
  161. package/dist/esm/{exceptions/domain → domain/exceptions}/type-primitive.exception.js +4 -5
  162. package/dist/esm/{exceptions/domain → domain/exceptions}/validation.exception.js +5 -5
  163. package/dist/esm/domain/index.d.ts +7 -0
  164. package/dist/esm/domain/index.js +6 -0
  165. package/dist/esm/domain/primitive/primitive-types.d.ts +1 -3
  166. package/dist/esm/domain/type/abstract-type.d.ts +6 -8
  167. package/dist/esm/domain/type/abstract-type.js +14 -22
  168. package/dist/esm/domain/type/array-type.d.ts +2 -1
  169. package/dist/esm/domain/type/array-type.js +16 -17
  170. package/dist/esm/domain/type/boolean-type.js +1 -1
  171. package/dist/esm/domain/type/composite/composite-brand.d.ts +4 -0
  172. package/dist/esm/domain/type/composite/composite-brand.js +11 -0
  173. package/dist/esm/domain/type/composite/composite-field.d.ts +7 -8
  174. package/dist/esm/domain/type/composite/composite-field.js +69 -46
  175. package/dist/esm/domain/type/composite/composite-issues.d.ts +6 -0
  176. package/dist/esm/domain/type/composite/composite-issues.js +31 -0
  177. package/dist/esm/domain/type/composite/composite.js +13 -17
  178. package/dist/esm/domain/type/date-type.d.ts +3 -2
  179. package/dist/esm/domain/type/date-type.js +39 -4
  180. package/dist/esm/domain/type/enum-type.js +1 -1
  181. package/dist/esm/domain/type/id-type.d.ts +1 -1
  182. package/dist/esm/domain/type/id-type.js +2 -2
  183. package/dist/esm/domain/type/index.d.ts +2 -4
  184. package/dist/esm/domain/type/index.js +1 -3
  185. package/dist/esm/domain/type/json-type.d.ts +4 -0
  186. package/dist/esm/domain/type/json-type.js +32 -3
  187. package/dist/esm/domain/type/number-type.js +1 -1
  188. package/dist/esm/domain/type/required/required-mark.d.ts +0 -1
  189. package/dist/esm/domain/type/string-type.js +1 -1
  190. package/dist/esm/domain/type/uuid-type.js +1 -1
  191. package/dist/esm/infrastructure/event/domain-event-bus.js +5 -5
  192. package/dist/esm/infrastructure/event/event-registry.js +4 -4
  193. package/dist/esm/infrastructure/event/in-memory/emitter.js +1 -1
  194. package/dist/esm/infrastructure/event/in-memory/in-memory-event-channel.d.ts +1 -1
  195. package/dist/esm/infrastructure/event/in-memory/in-memory-event-channel.js +5 -6
  196. package/dist/esm/{exceptions/infrastructure → infrastructure/exceptions}/event-delivery.exception.js +4 -4
  197. package/dist/esm/infrastructure/exceptions/infrastructure-exception-code.d.ts +6 -0
  198. package/dist/esm/infrastructure/exceptions/infrastructure-exception-code.js +7 -0
  199. package/dist/esm/infrastructure/exceptions/infrastructure.exception.d.ts +6 -0
  200. package/dist/esm/infrastructure/exceptions/infrastructure.exception.js +12 -0
  201. package/dist/esm/{exceptions/infrastructure → infrastructure/exceptions}/internal-error.exception.d.ts +1 -1
  202. package/dist/esm/infrastructure/exceptions/internal-error.exception.js +13 -0
  203. package/dist/esm/infrastructure/exceptions/kind/exception-classifier.d.ts +4 -0
  204. package/dist/esm/infrastructure/exceptions/kind/exception-classifier.js +30 -0
  205. package/dist/esm/infrastructure/exceptions/kind/exception-kind.d.ts +9 -0
  206. package/dist/esm/infrastructure/exceptions/kind/exception-kind.js +10 -0
  207. package/dist/esm/{exceptions/infrastructure → infrastructure/exceptions}/unknown-event.exception.js +4 -4
  208. package/dist/esm/infrastructure/index.d.ts +7 -2
  209. package/dist/esm/infrastructure/index.js +7 -1
  210. package/dist/esm/infrastructure/logging/console-logger.js +1 -1
  211. package/dist/{cjs → esm/shared}/exceptions/abstract.exception.d.ts +6 -4
  212. package/dist/esm/{exceptions → shared/exceptions}/abstract.exception.js +9 -16
  213. package/dist/esm/shared/index.d.ts +8 -0
  214. package/dist/esm/shared/index.js +5 -0
  215. package/dist/esm/shared/issue-collection-error.d.ts +9 -0
  216. package/dist/esm/shared/issue-collection-error.js +7 -0
  217. package/dist/esm/shared/issue-collector.d.ts +8 -0
  218. package/dist/esm/shared/issue-collector.js +37 -0
  219. package/package.json +7 -18
  220. package/dist/cjs/domain/type/equality/value-equality.d.ts +0 -7
  221. package/dist/cjs/domain/type/equality/value-equality.js +0 -41
  222. package/dist/cjs/domain/type/immutable/immutable-date.d.ts +0 -4
  223. package/dist/cjs/domain/type/immutable/immutable-date.js +0 -37
  224. package/dist/cjs/domain/type/immutable/immutable.d.ts +0 -4
  225. package/dist/cjs/domain/type/immutable/immutable.js +0 -19
  226. package/dist/cjs/domain/type/value-objects/value-objects.js +0 -70
  227. package/dist/cjs/exceptions/application/application.exception.d.ts +0 -6
  228. package/dist/cjs/exceptions/application/application.exception.js +0 -17
  229. package/dist/cjs/exceptions/domain/domain.exception.d.ts +0 -6
  230. package/dist/cjs/exceptions/domain/domain.exception.js +0 -17
  231. package/dist/cjs/exceptions/exception-code.d.ts +0 -15
  232. package/dist/cjs/exceptions/exception-code.js +0 -19
  233. package/dist/cjs/exceptions/index.d.ts +0 -14
  234. package/dist/cjs/exceptions/index.js +0 -30
  235. package/dist/cjs/exceptions/infrastructure/infrastructure.exception.d.ts +0 -6
  236. package/dist/cjs/exceptions/infrastructure/infrastructure.exception.js +0 -17
  237. package/dist/cjs/infrastructure/http/http-error-mapper.d.ts +0 -23
  238. package/dist/cjs/infrastructure/http/http-error-mapper.js +0 -63
  239. package/dist/esm/domain/type/equality/value-equality.d.ts +0 -7
  240. package/dist/esm/domain/type/equality/value-equality.js +0 -37
  241. package/dist/esm/domain/type/immutable/immutable-date.d.ts +0 -4
  242. package/dist/esm/domain/type/immutable/immutable-date.js +0 -33
  243. package/dist/esm/domain/type/immutable/immutable.d.ts +0 -4
  244. package/dist/esm/domain/type/immutable/immutable.js +0 -15
  245. package/dist/esm/domain/type/value-objects/value-objects.js +0 -66
  246. package/dist/esm/exceptions/application/aggregate-not-found.exception.js +0 -13
  247. package/dist/esm/exceptions/application/application.exception.d.ts +0 -6
  248. package/dist/esm/exceptions/application/application.exception.js +0 -13
  249. package/dist/esm/exceptions/application/not-allowed.exception.js +0 -13
  250. package/dist/esm/exceptions/domain/domain.exception.d.ts +0 -6
  251. package/dist/esm/exceptions/domain/domain.exception.js +0 -13
  252. package/dist/esm/exceptions/exception-code.d.ts +0 -15
  253. package/dist/esm/exceptions/exception-code.js +0 -16
  254. package/dist/esm/exceptions/index.d.ts +0 -14
  255. package/dist/esm/exceptions/index.js +0 -14
  256. package/dist/esm/exceptions/infrastructure/infrastructure.exception.d.ts +0 -6
  257. package/dist/esm/exceptions/infrastructure/infrastructure.exception.js +0 -13
  258. package/dist/esm/exceptions/infrastructure/internal-error.exception.js +0 -13
  259. package/dist/esm/infrastructure/http/http-error-mapper.d.ts +0 -23
  260. package/dist/esm/infrastructure/http/http-error-mapper.js +0 -59
  261. /package/dist/cjs/{exceptions/domain → domain/exceptions}/required-value.exception.d.ts +0 -0
  262. /package/dist/cjs/{exceptions/domain → domain/exceptions}/type-primitive.exception.d.ts +0 -0
  263. /package/dist/cjs/{exceptions/domain → domain/exceptions}/validation.exception.d.ts +0 -0
  264. /package/dist/cjs/domain/type/required/{required.d.ts → required-type.d.ts} +0 -0
  265. /package/dist/cjs/domain/type/required/{required.js → required-type.js} +0 -0
  266. /package/dist/cjs/{exceptions/infrastructure → infrastructure/exceptions}/event-delivery.exception.d.ts +0 -0
  267. /package/dist/cjs/{exceptions/infrastructure → infrastructure/exceptions}/unknown-event.exception.d.ts +0 -0
  268. /package/dist/cjs/{exceptions → shared/exceptions}/brand/exception-brand.d.ts +0 -0
  269. /package/dist/cjs/{exceptions → shared/exceptions}/brand/exception-brand.js +0 -0
  270. /package/dist/cjs/{exceptions → shared/exceptions}/received/received-value.d.ts +0 -0
  271. /package/dist/cjs/{exceptions → shared/exceptions}/received/received-value.js +0 -0
  272. /package/dist/esm/{exceptions/domain → domain/exceptions}/required-value.exception.d.ts +0 -0
  273. /package/dist/esm/{exceptions/domain → domain/exceptions}/type-primitive.exception.d.ts +0 -0
  274. /package/dist/esm/{exceptions/domain → domain/exceptions}/validation.exception.d.ts +0 -0
  275. /package/dist/esm/domain/type/required/{required.d.ts → required-type.d.ts} +0 -0
  276. /package/dist/esm/domain/type/required/{required.js → required-type.js} +0 -0
  277. /package/dist/esm/{exceptions/infrastructure → infrastructure/exceptions}/event-delivery.exception.d.ts +0 -0
  278. /package/dist/esm/{exceptions/infrastructure → infrastructure/exceptions}/unknown-event.exception.d.ts +0 -0
  279. /package/dist/esm/{exceptions → shared/exceptions}/brand/exception-brand.d.ts +0 -0
  280. /package/dist/esm/{exceptions → shared/exceptions}/brand/exception-brand.js +0 -0
  281. /package/dist/esm/{exceptions → shared/exceptions}/received/received-value.d.ts +0 -0
  282. /package/dist/esm/{exceptions → shared/exceptions}/received/received-value.js +0 -0
@@ -17,7 +17,9 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
17
17
  broker), so it must be a stable literal, unique per listener.
18
18
  - Subscribing listeners by a text name or calling them from a use case: the composition root
19
19
  subscribes them with `bus.subscribe(listener)`, which reads their `@ListensTo`.
20
- - Declaring `eventName()` as a method in an event: declare `static readonly eventName`.
20
+ - Declaring `eventName` as a method or an instance field in an event, or adding methods to it:
21
+ declare `static readonly eventName`; the base copies it into the event's `eventName` field, and
22
+ an event is plain data.
21
23
  - Importing the repository contracts from `@archi-code/ddd/application`: they live in `/domain`.
22
24
  - Do not invent API: there is no root `@archi-code/ddd` import, no `toPrimitives()` in the base
23
25
  classes, no generic `ValueObject` (a value object of several fields is `Composite({...})`), no
@@ -38,7 +40,7 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
38
40
  (`new OrderPlaced(id.value, customer.value)`): `occurredOn` becomes the system time and tests stop
39
41
  being deterministic.
40
42
  - 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
43
+ (`(json) => new OrderShipped(json.orderId as string)`): the event gets a new `eventId`, a listener
42
44
  cannot skip a redelivery, and `fromJSON` throws.
43
45
  - Subscribing a listener of an event the `EventRegistry` does not know: `bus.subscribe` refuses it
44
46
  with `UnknownEventException`; register every event a listener receives.
@@ -54,8 +56,10 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
54
56
  - A read query that loads aggregates through `AggregateFinder`, or a repository method that
55
57
  returns DTOs: reads go straight to the database through an application interface.
56
58
  - 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
+ - Importing an exception from another layer's subpath: each layer exports its own
60
+ (`ValidationException` from `/domain`, `AggregateNotFoundException` from `/application`,
61
+ `InternalErrorException` from `/infrastructure`); there is no `/exceptions` subpath and the
62
+ common base class is not exported (check the three layer bases, or use `ExceptionClassifier`).
59
63
  - Catching errors inside `handle` just to rethrow them as `ApplicationException`: let them
60
64
  propagate; `execute` keeps package exceptions and wraps the rest.
61
65
  - Calling `bus.publish` before `repository.save`, or publishing without `pullDomainEvents()`.
@@ -68,10 +72,10 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
68
72
  them with the aggregate in the same atomic write (an outbox) and a relay delivers them.
69
73
  - A listener that breaks when a channel delivers the same event twice: delivery is at least once;
70
74
  skip an `eventId` already processed.
71
- - Sending `event.toJSON()` of a domain event to another context: translate it to a published,
75
+ - Sending a domain event as it is (`{ ...event }`) to another context: translate it to a published,
72
76
  versioned contract.
73
- - Changing the data of a stored or published event without raising `eventVersion` (or the
74
- contract version).
77
+ - Changing the data of a stored or published event incompatibly under the same `eventName` (or
78
+ the same contract version) when no factory can read both shapes: publish a new event instead.
75
79
  - Repository methods with business names or rules (`pendingOlderThan`, `findOverdueInvoices`),
76
80
  `nextId()` in a repository, or a use case returning the aggregate to the controller.
77
81
  - Throwing `AggregateNotFoundException` from the domain or a repository: the use case throws it when
@@ -102,7 +106,7 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
102
106
  - Comparing VOs or entities with `===`: use `equals()`.
103
107
  - Calling `assertValid()`: it does not exist; the constructor already throws `ValidationException`.
104
108
  - 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.
109
+ invalid field hides the rest. Use `ValueObjectBuilder.build` so every field is reported.
106
110
  - Moving a value object's rule to the request schema or the controller (`isEmail` in a DTO
107
111
  validator instead of the `Email` value object): the adapter may check format for the user
108
112
  (empty, length) [IDDD ch. 4], but the value object owns the rule and always checks it.
@@ -156,17 +160,20 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
156
160
  - Converting an aggregate to or from primitives outside the aggregate (a domain service, a
157
161
  hand-written mapper reading its fields): the aggregate owns `toPrimitives()` and
158
162
  `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.
163
+ - Declaring a `version` field in an aggregate, or putting it in `toPrimitives()`: the version is
164
+ the repository's, which keeps it in its own `WeakMap` and stores it next to the primitives.
161
165
  - A repository (also the in-memory one of the tests) that saves with `upsert` and no version
162
166
  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.
167
+ - A repository that forgets to remember `version + 1` after a successful write (the next save of
168
+ the same object becomes a false conflict), or the stored version on load (every load looks new,
169
+ and the first save conflicts).
170
+ - Exposing the version to domain or application code: use cases load, change and save; only the
171
+ repository knows it.
172
+ - Creating events without the id of their aggregate as a payload field (`readonly orderId: string`),
173
+ or generating `eventId`/`occurredOn` yourself for a new event: the base does it.
174
+ - Putting value objects, `Date`s, `undefined` or class instances in an event's fields (nothing
175
+ checks it at run time): each event declares only the primitive data it needs (`id.value`,
176
+ money as `{ amount, currency }`, `date.toISOString()`).
170
177
  - Wrapping or translating an error (`InternalErrorException`, `ConcurrencyException`,
171
178
  `NotAllowedException`, `AggregateNotFoundException`) without `{ cause: error }`.
172
179
  - `JsonType` with `{}` or with an array: throws `TypePrimitiveException` (it must be a non-empty
@@ -183,7 +190,7 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
183
190
  - Logging from the domain, or importing a logging library (pino, winston, `console`) in a use case:
184
191
  inject the `Logger` port and choose the provider in the composition root.
185
192
  - 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).
193
+ (`DomainEventBus` with a logger already does; an entry point logs an `Internal` failure).
187
194
  - Interpolating data into the message (`` `order ${id} failed` ``) or logging passwords, tokens or
188
195
  personal data that the entry does not need: keep a stable message and put only what is needed in
189
196
  the context.
@@ -4,27 +4,27 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
4
4
 
5
5
  | Class | Layer | May depend on | Never |
6
6
  | ------------------------------------------------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
7
- | Value object, composite | domain | other value objects, `@archi-code/validation`, exceptions | mutate, know about storage or transport |
7
+ | Value object, composite | domain | other value objects, `@archi-code/validation`, domain exceptions | mutate, know about storage or transport |
8
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
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
10
  | Domain event (`OrderPlaced`) | domain | primitives taken from value objects | behavior, value objects or the whole state in its payload |
11
11
  | Repository contract (`AggregateFinder`…) | domain | the aggregate | business logic or queries, an all-in-one `Repository` |
12
12
  | Domain service | domain | aggregates, value objects, specifications, repository contracts (to read), domain ports | I/O code, transactions, publishing |
13
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 |
14
+ | Use case (`PlaceOrder`) | application | domain, ports and exceptions | import another use case or a listener, return an aggregate |
15
15
  | Listener (`ReserveStockOnOrderPlaced`) | application | one use case (by constructor), the event | business rules, saving aggregates, calling several use cases |
16
16
  | Port (`EventBus`, `Clock`, `Logger`, your own) | application | domain types | code |
17
17
  | Read query (`OrderSummaries`, `StaleOrders`) | application (interface), infrastructure | DTOs and ids, `@archi-code/criteria` | loading aggregates, repositories, business rules |
18
18
  | Adapter (repository, event channel, ACL translator, logger provider) | infrastructure | domain, application, exceptions, drivers | business rules |
19
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 |
20
+ | Entry point (controller, consumer, scheduler, CLI) | interfaces | application, `ExceptionClassifier` and `ExceptionKind`; domain types only if unavoidable | business rules, repositories or other adapters |
21
21
  | Composition root (`composition.ts`) | per context | everything | being imported by any layer |
22
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).
23
+ - Dependencies only point inwards: the domain imports only `@archi-code/ddd/domain` and
24
+ `@archi-code/validation` (not "nothing external" as the suite says: these are the package
25
+ convention); the application adds `@archi-code/ddd/application`; only infrastructure and the
26
+ composition root import `@archi-code/ddd/infrastructure`, except that an entry point imports
27
+ `ExceptionClassifier` and `ExceptionKind` from it to translate errors (they hold no adapter).
28
28
  Entry points (`interfaces/`: controllers, message consumers, schedulers, CLI) call use cases
29
29
  only. The composition root of each context is the only place that builds and connects everything
30
30
  (repositories, event channel, `DomainEventBus`, `EventRegistry`, listeners) and reads
@@ -53,8 +53,9 @@ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
53
53
  - Check the direction with dependency-cruiser (`tsPreCompilationDeps: true`) and the suite's
54
54
  `ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs`, which already allows the `@archi-code/ddd`
55
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`).
56
+ - Import from the layer, never from the package root (there is none): each exception comes from
57
+ the subpath of its layer (`DomainException` from `/domain`, `AggregateNotFoundException` from
58
+ `/application`, `InternalErrorException` from `/infrastructure`); there is no `/exceptions`.
58
59
  - Logic two use cases share never goes in a use case: a business rule becomes a method of the
59
60
  aggregate or a domain service; a technical step becomes a port. When an action must follow
60
61
  another, the first records an event and a listener runs the second.
@@ -8,7 +8,7 @@
8
8
 
9
9
  [Fuera de los libros: convención de @archi-code/ddd]
10
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).
11
+ * La raíz no se escribe: extiende `AggregateRoot<Id, Event>` y llama a `super(id)`; el agregado no declara ni mapea ninguna `version`: la de la concurrencia optimista es solo del repositorio, que recuerda la de cada agregado cargado o guardado en un `WeakMap<Order, number>` y la actualiza solo tras escribir con éxito; el caso de uso nunca la ve.
12
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
13
  * Las identidades nuevas salen del value object (`OrderId.generate()`), no del repositorio.
14
14
  * Los métodos del dominio reciben `clock: { now(): Date }` y lo pasan a los eventos (`{ clock }`).
@@ -68,7 +68,7 @@ Guía completa en `ddd-skills/ddd-agregados/references/implementacion-ts.md`. Lo
68
68
  * Partes internas como **value objects** siempre que se puedan reemplazar enteras [IDDD cap. 10].
69
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
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`).
71
+ * **Concurrencia optimista** con versión en la raíz [IDDD cap. 10]: con el paquete la guarda el repositorio (un `WeakMap` con la versión de cada agregado que cargó o guardó, almacenada junto a los primitivos, nunca dentro) y la comprueba al guardar y al borrar (`ConcurrencyException`) `[Fuera de los libros: convención de @archi-code/ddd]`.
72
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
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
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`.
@@ -32,12 +32,12 @@ export class StockReserved extends EventBase {
32
32
  static readonly eventName = 'warehouse.stock.StockReserved';
33
33
 
34
34
  constructor(
35
- stockItemId: string,
35
+ readonly stockItemId: string,
36
36
  readonly orderId: string,
37
37
  readonly reservationId: string,
38
38
  metadata?: EventMetadata,
39
39
  ) {
40
- super(stockItemId, metadata);
40
+ super(metadata);
41
41
  }
42
42
  }
43
43
  ```
@@ -45,9 +45,8 @@ export class StockReserved extends EventBase {
45
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
46
 
47
47
  ```ts
48
- import { ValueObjects } from '@archi-code/ddd/domain';
49
48
  import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
50
- import { UseCase } from '@archi-code/ddd/application';
49
+ import { UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
51
50
  import type { Clock, EventBus } from '@archi-code/ddd/application';
52
51
 
53
52
  interface StockReservationInput {
@@ -74,7 +73,7 @@ export class RegisterStockReservation extends UseCase<
74
73
  }
75
74
 
76
75
  protected toCommand(input: StockReservationInput): StockReservationCommand {
77
- return ValueObjects.build({
76
+ return ValueObjectBuilder.build({
78
77
  orderId: () => new OrderId(input.orderId),
79
78
  reservationId: () => new ReservationId(input.reservationId),
80
79
  });
@@ -127,7 +126,7 @@ const orders = new InMemoryOrderRepository();
127
126
  const registry = new EventRegistry<OrderEvent | StockReserved>().register(
128
127
  StockReserved,
129
128
  (json) =>
130
- new StockReserved(json.aggregateId, json.orderId as string, json.reservationId as string, json),
129
+ new StockReserved(json.stockItemId as string, json.orderId as string, json.reservationId as string, json),
131
130
  );
132
131
  const channel = new InMemoryEventChannel();
133
132
  const bus = new DomainEventBus<OrderEvent | StockReserved>(channel, registry);
@@ -15,9 +15,8 @@ No contradice la regla de una transacción por agregado: el creador no se modifi
15
15
  `Order`, `OrderId`, `OrderLines` y `ShippingAddress` son los de `references/implementacion-ts.md`:
16
16
 
17
17
  ```ts
18
- import { AggregateRoot, BooleanType, Required, StringType } from '@archi-code/ddd/domain';
18
+ import { AggregateRoot, BooleanType, DomainException, Required, StringType } from '@archi-code/ddd/domain';
19
19
  import type { PrimitiveTypes } from '@archi-code/ddd/domain';
20
- import { DomainException } from '@archi-code/ddd/exceptions';
21
20
  import { Validations } from '@archi-code/validation';
22
21
 
23
22
  export class CustomerName extends Required(StringType) {}
@@ -86,11 +85,9 @@ export class Customer extends AggregateRoot<CustomerId> {
86
85
  La regla de creación ("un cliente bloqueado no puede realizar pedidos") vive en `Customer`. El caso de uso lee el cliente con `AggregateFinder<Customer>`, genera la identidad nueva con `OrderId.generate()` y guarda solo el pedido con `AggregateSaver<Order>`; `Customer` no se guarda porque no ha cambiado:
87
86
 
88
87
  ```ts
89
- import { ValueObjects } from '@archi-code/ddd/domain';
90
88
  import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
91
- import { UseCase } from '@archi-code/ddd/application';
89
+ import { AggregateNotFoundException, UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
92
90
  import type { Clock, EventBus } from '@archi-code/ddd/application';
93
- import { AggregateNotFoundException } from '@archi-code/ddd/exceptions';
94
91
 
95
92
  interface PlaceOrderInput {
96
93
  customerId: string;
@@ -115,7 +112,7 @@ export class PlaceOrder extends UseCase<PlaceOrderInput, PlaceOrderCommand, stri
115
112
  }
116
113
 
117
114
  protected toCommand(input: PlaceOrderInput): PlaceOrderCommand {
118
- return ValueObjects.build({
115
+ return ValueObjectBuilder.build({
119
116
  customerId: () => new CustomerId(input.customerId),
120
117
  lines: () => OrderLines.fromPrimitives({ items: input.lines }),
121
118
  shippingAddress: () => ShippingAddress.fromPrimitives(input.shippingAddress),
@@ -173,7 +170,7 @@ describe('Customer.placeOrder', () => {
173
170
  );
174
171
  const order = customer.placeOrder(OrderId.generate(), lines, address, clock);
175
172
  expect(order.customerId.equals(customer.id)).toBe(true);
176
- expect(order.pullDomainEvents().map((event) => event.eventName())).toEqual(['OrderPlaced']);
173
+ expect(order.pullDomainEvents().map((event) => event.eventName)).toEqual(['OrderPlaced']);
177
174
  });
178
175
 
179
176
  it('refuses an order from a blocked customer', () => {
@@ -192,5 +189,5 @@ describe('Customer.placeOrder', () => {
192
189
 
193
190
  ## Cuándo no
194
191
 
195
- * Para reconstituir desde base de datos: eso es `static fromPrimitives(primitives)`, que el repositorio llama directamente (`Order.fromPrimitives(row).restoreVersion(row.version)`), sin reglas de creación ni eventos.
192
+ * Para reconstituir desde base de datos: eso es `static fromPrimitives(primitives)`, que el repositorio llama directamente (`Order.fromPrimitives(row.primitives)`; la versión de la fila la recuerda el repositorio en su `WeakMap`, no el agregado), sin reglas de creación ni eventos.
196
193
  * "Fábricas" genéricas que reciben un DTO y rellenan campos: un constructor con setters disfrazado.
@@ -4,7 +4,7 @@ Contenido: 1. Raíz base · 2. Construcción paso a paso · 3. Agregado completo
4
4
 
5
5
  ## 1. Raíz base
6
6
 
7
- La raíz no se escribe a mano: se extiende `AggregateRoot<Id, Event>` de `@archi-code/ddd/domain`, que ya trae lo que IDDD cap. 10 describe para la raíz (identidad, igualdad por identidad, versión para la concurrencia optimista y lista de eventos registrados) `[Fuera de los libros: convención de @archi-code/ddd]`.
7
+ La raíz no se escribe a mano: se extiende `AggregateRoot<Id, Event>` de `@archi-code/ddd/domain`, que ya trae lo que IDDD cap. 10 describe para la raíz salvo la versión (identidad, igualdad por identidad y lista de eventos registrados); la versión para la concurrencia optimista la guarda el repositorio, no el agregado (sección 5) `[Fuera de los libros: convención de @archi-code/ddd]`.
8
8
 
9
9
  ```ts
10
10
  import { AggregateRoot } from '@archi-code/ddd/domain';
@@ -24,10 +24,7 @@ La clase completa está en la sección 3. Lo que da la base:
24
24
  * `readonly id` y `equals(other)` (misma clase y mismo id) de `Entity<Id>`; un id sin valor lanza `RequiredValueException`.
25
25
  * `protected record(event)`: guarda el evento congelado (los hechos no cambian [IDDD cap. 8]).
26
26
  * `pullDomainEvents()`: devuelve los eventos en orden (tipados como `Event[]`) y vacía la lista; lo llama el caso de uso tras guardar.
27
- * `version`: `0` si es nuevo; no es un campo del agregado ni aparece en `PrimitiveTypes`.
28
- * `restoreVersion(v)` y `markPersisted()`: solo para repositorios (al cargar y tras escribir).
29
-
30
- Sin `markPersisted`, el objeto en memoria conservaría la versión antigua tras guardar y un segundo `save` del mismo objeto (por ejemplo, dos comandos seguidos en el mismo proceso) daría un falso conflicto de concurrencia `[Fuera de los libros]`. Los repositorios que sigan el contrato lo llaman siempre, también el `InMemoryOrderRepository` que el proyecto escribe en `test/support/` para los tests.
27
+ La base no trae versión: el agregado no la declara, no la mapea en `toPrimitives()` y el caso de uso nunca la ve. Cada repositorio recuerda la versión de cada agregado que cargó o guardó en un `WeakMap<Order, number>` propio y la actualiza solo cuando la escritura ha ido bien; así un segundo `save` del mismo objeto (por ejemplo, dos comandos seguidos en el mismo proceso) no da un falso conflicto de concurrencia `[Fuera de los libros: convención de @archi-code/ddd]`. Lo cumplen todos los repositorios, también el `InMemoryOrderRepository` que el proyecto escribe en `test/support/` para los tests.
31
28
 
32
29
  Por qué registrar en vez de publicar: IDDD publica desde el agregado con un publicador estático [IDDD cap. 8]. Registrar y dejar que la aplicación publique no requiere un singleton global en el dominio y, con una bandeja de salida (outbox), evita entregar eventos de cambios que no llegaron a guardarse. *DDD in PHP* discute ambas variantes [PHP: Domain Events]. Con el paquete, el caso de uso llama a `await this.orders.save(order)` y después a `events.publish(order.pullDomainEvents())`; la aplicación no abre transacciones, porque la transacción es del repositorio. Si `publish` falla tras `save`, el evento puede perderse; cuando eso no es admisible, el repositorio guarda el agregado y sus eventos pendientes en la misma escritura atómica (outbox) y un proceso aparte los entrega por un `EventChannel`, y el caso de uso no cambia `[Fuera de los libros: convención de @archi-code/ddd]`.
33
30
 
@@ -55,7 +52,7 @@ import {
55
52
  Required,
56
53
  StringType,
57
54
  } from '@archi-code/ddd/domain';
58
- import { DomainException } from '@archi-code/ddd/exceptions';
55
+ import { DomainException } from '@archi-code/ddd/domain';
59
56
  import { Validations } from '@archi-code/validation';
60
57
 
61
58
  export class OrderId extends IdType<'OrderId'> {}
@@ -191,9 +188,8 @@ export class OrderStatus extends Required(StatusType) {
191
188
  Eventos (hechos en pasado, con primitivos tomados de los value objects) y errores de negocio:
192
189
 
193
190
  ```ts
194
- import { EventBase } from '@archi-code/ddd/domain';
191
+ import { DomainException, EventBase } from '@archi-code/ddd/domain';
195
192
  import type { EventMetadata } from '@archi-code/ddd/domain';
196
- import { DomainException } from '@archi-code/ddd/exceptions';
197
193
 
198
194
  export interface MoneyData {
199
195
  readonly amount: number;
@@ -210,13 +206,13 @@ export class OrderPlaced extends EventBase {
210
206
  static readonly eventName = 'OrderPlaced';
211
207
 
212
208
  constructor(
213
- orderId: string,
209
+ readonly orderId: string,
214
210
  readonly customerId: string,
215
211
  readonly lines: readonly OrderLineData[],
216
212
  readonly total: MoneyData,
217
213
  metadata?: EventMetadata,
218
214
  ) {
219
- super(orderId, metadata);
215
+ super(metadata);
220
216
  }
221
217
  }
222
218
 
@@ -224,14 +220,14 @@ export class OrderLineAdded extends EventBase {
224
220
  static readonly eventName = 'OrderLineAdded';
225
221
 
226
222
  constructor(
227
- orderId: string,
223
+ readonly orderId: string,
228
224
  readonly sku: string,
229
225
  readonly quantity: number,
230
226
  readonly unitPrice: MoneyData,
231
227
  readonly total: MoneyData,
232
228
  metadata?: EventMetadata,
233
229
  ) {
234
- super(orderId, metadata);
230
+ super(metadata);
235
231
  }
236
232
  }
237
233
 
@@ -239,11 +235,11 @@ export class OrderConfirmed extends EventBase {
239
235
  static readonly eventName = 'OrderConfirmed';
240
236
 
241
237
  constructor(
242
- orderId: string,
238
+ readonly orderId: string,
243
239
  readonly total: MoneyData,
244
240
  metadata?: EventMetadata,
245
241
  ) {
246
- super(orderId, metadata);
242
+ super(metadata);
247
243
  }
248
244
  }
249
245
 
@@ -251,13 +247,13 @@ export class OrderCancelled extends EventBase {
251
247
  static readonly eventName = 'OrderCancelled';
252
248
 
253
249
  constructor(
254
- orderId: string,
250
+ readonly orderId: string,
255
251
  readonly reason: string,
256
252
  readonly reservationId: string | null,
257
253
  readonly paymentId: string | null,
258
254
  metadata?: EventMetadata,
259
255
  ) {
260
- super(orderId, metadata);
256
+ super(metadata);
261
257
  }
262
258
  }
263
259
 
@@ -265,13 +261,13 @@ export class PaymentAuthorizationInsufficient extends EventBase {
265
261
  static readonly eventName = 'PaymentAuthorizationInsufficient';
266
262
 
267
263
  constructor(
268
- orderId: string,
264
+ readonly orderId: string,
269
265
  readonly paymentId: string,
270
266
  readonly authorized: MoneyData,
271
267
  readonly required: MoneyData,
272
268
  metadata?: EventMetadata,
273
269
  ) {
274
- super(orderId, metadata);
270
+ super(metadata);
275
271
  }
276
272
  }
277
273
 
@@ -474,7 +470,7 @@ Cómo encaja con las reglas:
474
470
 
475
471
  * `customerId` es una referencia por identidad (regla 3); `stockReservation` es la identidad de otro agregado de otro contexto, y `paymentAuthorization` un value object con lo mínimo que el pedido necesita de Pagos.
476
472
  * Las líneas son un value object (`OrderLines`, parte reemplazable entera); `addLine` sustituye la colección por la que devuelve `withLine`, y la regla de SKU único la comprueba el propio value object.
477
- * Constructor privado, una factoría de negocio (`place`, que registra `OrderPlaced`) y `fromPrimitives`, que reconstruye sin reglas de creación ni eventos; `toPrimitives()` devuelve solo datos planos. La conversión es solo del propio agregado (instantánea): el repositorio, cuyo constructor recibe solo las dependencias de su tecnología, llama directamente a `order.toPrimitives()` al escribir y a `Order.fromPrimitives(row).restoreVersion(row.version)` al leer, sin mapper ni comprobación de forma en medio, y no lee los campos. Quien añade un campo lo añade a `toPrimitives`/`fromPrimitives`, y el spec del agregado comprueba la ida y vuelta `[Fuera de los libros: convención de @archi-code/ddd]`.
473
+ * Constructor privado, una factoría de negocio (`place`, que registra `OrderPlaced`) y `fromPrimitives`, que reconstruye sin reglas de creación ni eventos; `toPrimitives()` devuelve solo datos planos. La conversión es solo del propio agregado (instantánea): el repositorio, cuyo constructor recibe solo las dependencias de su tecnología, llama directamente a `order.toPrimitives()` al escribir y a `Order.fromPrimitives(row.primitives)` al leer (la versión de la fila la guarda en su `WeakMap`, no en el agregado), sin mapper ni comprobación de forma en medio, y no lee los campos. Quien añade un campo lo añade a `toPrimitives`/`fromPrimitives`, y el spec del agregado comprueba la ida y vuelta `[Fuera de los libros: convención de @archi-code/ddd]`.
478
474
  * Cada método recibe el reloj (`clock: { now(): Date }`) y lo pasa a los eventos (`{ clock }`): el dominio no llama a `new Date()` y los tests son deterministas.
479
475
 
480
476
  ### Comandos frente a hechos
@@ -483,7 +479,7 @@ Cómo encaja con las reglas:
483
479
 
484
480
  * Los métodos que reaccionan a hechos son **idempotentes** y **no lanzan errores de negocio**.
485
481
  * Si el hecho no encaja con las reglas, el agregado registra un evento con nombre del negocio (`PaymentAuthorizationInsufficient`) y otra política decide la compensación (anular la autorización, avisar al cliente). Así el problema queda visible en el modelo en vez de perdido en una cola de errores.
486
- * Los errores de negocio con excepción (`DomainException` con su código) quedan para comandos de usuario (`cancel`, `addLine`), donde alguien puede recibir la respuesta (`HttpErrorMapper` los traduce a 422 salvo que se le indique otro estado para su código).
482
+ * Los errores de negocio con excepción (`DomainException` con su código) quedan para comandos de usuario (`cancel`, `addLine`), donde alguien puede recibir la respuesta (`ExceptionClassifier.classify(error)` los clasifica como `ExceptionKind.Invalid`, 422 en HTTP, y el punto de entrada puede dar otro estado a su código).
487
483
 
488
484
  Qué protege este agregado (sus invariantes, regla 1):
489
485
  * El total es siempre la suma de sus líneas (las líneas son un value object con su regla).
@@ -499,9 +495,8 @@ No inyectes repositorios ni servicios en el agregado. Busca antes lo necesario y
499
495
  El caso de uso construye los valores en `toCommand` y se los pasa al agregado en `handle`:
500
496
 
501
497
  ```ts
502
- import { ValueObjects } from '@archi-code/ddd/domain';
503
498
  import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
504
- import { UseCase } from '@archi-code/ddd/application';
499
+ import { UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
505
500
  import type { Clock, EventBus } from '@archi-code/ddd/application';
506
501
 
507
502
  interface PaymentAuthorizationInput {
@@ -531,7 +526,7 @@ export class RegisterPaymentAuthorization extends UseCase<
531
526
  }
532
527
 
533
528
  protected toCommand(input: PaymentAuthorizationInput): PaymentAuthorizationCommand {
534
- return ValueObjects.build({
529
+ return ValueObjectBuilder.build({
535
530
  orderId: () => new OrderId(input.orderId),
536
531
  authorization: () =>
537
532
  PaymentAuthorization.fromPrimitives({
@@ -560,7 +555,7 @@ El agregado recibe valores, no repositorios. Como registra un hecho (lo ejecuta
560
555
 
561
556
  ## 5. Concurrencia optimista
562
557
 
563
- * Versión en la raíz, incrementada con cada comando que cambie estado en cualquier parte del agregado [IDDD cap. 10]. Con el paquete es la `version` de `AggregateRoot`: el agregado no la declara ni la mapea, y solo la tocan los repositorios (`restoreVersion` al cargar, `markPersisted` tras escribir) `[Fuera de los libros: convención de @archi-code/ddd]`.
558
+ * Versión en la raíz, incrementada con cada comando que cambie estado en cualquier parte del agregado [IDDD cap. 10]. Con el paquete la versión es solo del repositorio: el agregado no la declara ni la mapea y el caso de uso nunca la ve. El repositorio recuerda la versión de cada agregado que cargó o guardó en un `WeakMap<Order, number>` (`0` si no la conoce: es nuevo), escribe solo donde la versión guardada es esa, guarda `version + 1` junto a los primitivos (nunca dentro) y actualiza el `WeakMap` solo cuando la escritura ha ido bien (en SQL, tras el `COMMIT`) `[Fuera de los libros: convención de @archi-code/ddd]`.
564
559
  * Si las partes internas son entidades y el ORM no detecta su cambio como cambio de la raíz, es una señal más para reducir el agregado a raíz más value objects [IDDD cap. 10].
565
560
  * En almacenes de documentos o clave-valor el agregado se guarda entero y la versión se comprueba en una sola operación [IDDD cap. 10].
566
561
 
@@ -572,7 +567,7 @@ UPDATE orders
572
567
  WHERE id = $1 AND version = $4;
573
568
  ```
574
569
 
575
- Si la sentencia no afecta a ninguna fila, el repositorio lanza `new ConcurrencyException('Order', order.id.value, order.version)` (de `@archi-code/ddd/exceptions`, HTTP 409 con `HttpErrorMapper`; el nombre del agregado va como texto literal porque los minificadores renombran las clases); si escribe, llama a `order.markPersisted()`. En los tests lo cumple el `InMemoryOrderRepository` que el proyecto escribe en `test/support/`; el repositorio real y su suite de contrato están en `ddd-repositorios-persistencia`.
570
+ `$4` es la versión que el repositorio recordaba (`this.versions.get(order) ?? 0`). Si la sentencia no afecta a ninguna fila, el repositorio lanza `new ConcurrencyException('Order', order.id.value, version)` (de `@archi-code/ddd/application`; `ExceptionKind.Conflict`, 409 en HTTP; el nombre del agregado va como texto literal porque los minificadores renombran las clases); un agregado nuevo (versión `0`) se inserta y un id que ya existe es también un conflicto. Si escribe, hace `this.versions.set(order, version + 1)`. En los tests lo cumple el `InMemoryOrderRepository` que el proyecto escribe en `test/support/`; el repositorio real y su suite de contrato están en `ddd-repositorios-persistencia`.
576
571
 
577
572
  Reintentar automáticamente solo es seguro si el comando es idempotente o se vuelve a evaluar contra el estado recargado: con el paquete, un bucle con límite en `handle` en el que cada intento vuelve a cargar el agregado y vuelve a guardarlo (unos pocos intentos y después se relanza); un caso de uso que ejecuta un listener no reintenta por su cuenta, lo hace la reentrega del canal `[Fuera de los libros: convención de @archi-code/ddd]`. Ejemplo en `ddd-skills/ddd-repositorios-persistencia/references/transacciones-ts.md`.
578
573
 
@@ -673,7 +668,7 @@ describe('Order', () => {
673
668
  expect(order.isConfirmed()).toBe(false);
674
669
  order.registerStockReservation(aReservation(), clock);
675
670
  expect(order.isConfirmed()).toBe(true);
676
- expect(order.pullDomainEvents().map((event) => event.eventName())).toContain('OrderConfirmed');
671
+ expect(order.pullDomainEvents().map((event) => event.eventName)).toContain('OrderConfirmed');
677
672
  });
678
673
 
679
674
  it('records an insufficient authorization without throwing and without confirming', () => {
@@ -681,7 +676,7 @@ describe('Order', () => {
681
676
  order.registerStockReservation(aReservation(), clock);
682
677
  order.registerPaymentAuthorization(anAuthorizationOf(1000), clock);
683
678
  expect(order.isConfirmed()).toBe(false);
684
- expect(order.pullDomainEvents().map((event) => event.eventName())).toContain(
679
+ expect(order.pullDomainEvents().map((event) => event.eventName)).toContain(
685
680
  'PaymentAuthorizationInsufficient',
686
681
  );
687
682
  });
@@ -8,7 +8,7 @@
8
8
 
9
9
  [Fuera de los libros: convención de @archi-code/ddd]
10
10
 
11
- * El dominio importa `@archi-code/ddd/domain`, `@archi-code/ddd/exceptions` y `@archi-code/validation` (no "nada externo"); la aplicación añade `@archi-code/ddd/application`; solo infraestructura y el composition root importan `@archi-code/ddd/infrastructure`, salvo `HttpErrorMapper` en los puntos de entrada.
11
+ * Cada subpath del paquete exporta las excepciones de su capa y cada capa importa solo el suyo y los interiores: el dominio importa `@archi-code/ddd/domain` (con `DomainException`, `ValidationException`…) y `@archi-code/validation` (no "nada externo"); la aplicación añade `@archi-code/ddd/application` (con `AggregateNotFoundException`, `ConcurrencyException`, `NotAllowedException`…); solo infraestructura y el composition root usan `@archi-code/ddd/infrastructure` (con `InternalErrorException`), salvo los puntos de entrada, que traducen los errores con `ExceptionClassifier` de `@archi-code/ddd/infrastructure`.
12
12
  * No se escriben clases base ni puertos genéricos en el proyecto: `AggregateRoot`, `Entity`, `EventBase`, los tipos de value object y `Composite` vienen de `@archi-code/ddd/domain`; los contratos de repositorio son los roles `AggregateFinder`, `AggregateSaver` y `AggregateRemover` de `@archi-code/ddd/domain` (no hay un `OrderRepository` propio); `EventBus` y `Clock` vienen de `@archi-code/ddd/application` y `EventChannel` de `@archi-code/ddd/infrastructure`. No hay puerto de transacciones: el repositorio es responsable de la transacción y de todo lo relacionado con la persistencia.
13
13
  * Un mecanismo de mensajería es solo un `EventChannel` en `infrastructure/messaging/`; el bus es siempre `DomainEventBus`, y cambiar de tecnología cambia una línea del composition root. `DomainEventBus`, `EventRegistry` y el puerto `EventChannel` (con `ChannelHandler` y `ChannelSubscription`) se importan de `@archi-code/ddd/infrastructure`: son cableado de infraestructura. La aplicación solo ve el contrato para publicar (`EventBus`) y para escuchar (`EventListener` + `@ListensTo`) de `@archi-code/ddd/application`.
14
14
  * El composition root de cada contexto es el único que construye y conecta todo (repositorios, canal, `DomainEventBus`, suscripción de listeners con `bus.subscribe`) y lee `process.env`; nadie lo importa. El `dependency-cruiser.cjs` de esta guía ya aplica estas reglas.
@@ -37,10 +37,10 @@ infrastructure (adaptadores salida) ─┘ ▲
37
37
 
38
38
  | Zona | Contiene | Puede depender de | Nunca |
39
39
  |---|---|---|---|
40
- | `domain` | Agregados (`AggregateRoot`), entidades (`Entity`), value objects, eventos de dominio (`EventBase`), servicios de dominio, puertos hacia servicios externos (`ProductPricing`); los contratos de repositorio son los roles `AggregateFinder`/`AggregateSaver`/`AggregateRemover` del paquete | Nada externo salvo `@archi-code/ddd/domain`, `@archi-code/ddd/exceptions` y `@archi-code/validation` | Framework web, ORM, cliente HTTP, `process.env`, logger |
40
+ | `domain` | Agregados (`AggregateRoot`), entidades (`Entity`), value objects, eventos de dominio (`EventBase`), servicios de dominio, puertos hacia servicios externos (`ProductPricing`); los contratos de repositorio son los roles `AggregateFinder`/`AggregateSaver`/`AggregateRemover` del paquete | Nada externo salvo `@archi-code/ddd/domain` (también sus excepciones) y `@archi-code/validation` | Framework web, ORM, cliente HTTP, `process.env`, logger |
41
41
  | `application` | Casos de uso (`UseCase`), listeners (`EventListener` + `@ListensTo`), consultas de lectura con sus DTOs, puertos de salida no de dominio (`EventBus` y `Clock` del paquete; los tuyos, como `Warehouse`) | `domain`, `@archi-code/ddd/application` | Express/Fastify, SQL, detalles de mensajería |
42
42
  | `infrastructure` | Repositorios concretos (`MongoOrderRepository`), ACL, canales de eventos (`EventChannel`) y el cableado de eventos (`DomainEventBus`, `EventRegistry`, en el composition root), clientes HTTP; el repositorio se encarga de la atomicidad de cada escritura, la versión, la conexión y los errores del driver | `domain`, `application`, `@archi-code/ddd/infrastructure` | Lógica de negocio |
43
- | `interfaces` | Controladores REST, consumidores de mensajes, CLI, resolvers GraphQL | `application`, `HttpErrorMapper` (y tipos de `domain` solo si es imprescindible) | Lógica de negocio, acceso directo a repositorios |
43
+ | `interfaces` | Controladores REST, consumidores de mensajes, CLI, resolvers GraphQL | `application`, las excepciones de `@archi-code/ddd/domain` y `/application`, `ExceptionClassifier` de `@archi-code/ddd/infrastructure` (y tipos de `domain` solo si es imprescindible) | Lógica de negocio, acceso directo a repositorios |
44
44
 
45
45
  Responsabilidades de los servicios de aplicación según IDDD: sin lógica de dominio, controlan transacciones y seguridad (en el paquete, la seguridad con `NotAllowedException`; la transacción pasa al adaptador del repositorio, que hace atómico cada `save` y `remove` `[Fuera de los libros: convención de @archi-code/ddd]`), pueden enviar notificaciones; son clientes directos del modelo y deben ser **finos**. Si crecen, la lógica de dominio se está escapando y el modelo se vuelve anémico [IDDD cap. 4]. Detalle en `ddd-skills/ddd-servicios-capa-aplicacion/GUIA.md`.
46
46
 
@@ -57,7 +57,7 @@ En la interfaz se valida formato (campo obligatorio, longitud), no conocimiento
57
57
 
58
58
  ## REST sin exponer el modelo
59
59
 
60
- Exponer el modelo directamente por REST produce interfaces frágiles: cada cambio del modelo rompe a los clientes. IDDD prefiere separar el modelo de la interfaz: recursos diseñados por casos de uso, construidos a partir de uno o varios agregados [IDDD cap. 4]. En TypeScript: el controlador comprueba la forma de la petición, llama a `execute` del caso de uso con su input, responde con lo que este devuelve (un id, un DTO o nada; nunca el agregado) y traduce los errores con `HttpErrorMapper` de `@archi-code/ddd/infrastructure` (404, 403, 409, 422…). Ejemplo en `ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md`.
60
+ Exponer el modelo directamente por REST produce interfaces frágiles: cada cambio del modelo rompe a los clientes. IDDD prefiere separar el modelo de la interfaz: recursos diseñados por casos de uso, construidos a partir de uno o varios agregados [IDDD cap. 4]. En TypeScript: el controlador comprueba la forma de la petición, llama a `execute` del caso de uso con su input, responde con lo que este devuelve (un id, un DTO o nada; nunca el agregado) y traduce los errores según el `ExceptionKind` de `ExceptionClassifier.classify(error)` (`@archi-code/ddd/infrastructure`) a su protocolo (404, 403, 409, 422… en HTTP). Ejemplo en `ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md`.
61
61
 
62
62
  ## Señales de alarma
63
63
 
@@ -3,8 +3,10 @@
3
3
  * Uso: npx depcruise src --config .dependency-cruiser.cjs
4
4
  * Funciona con src/contexts/<contexto>/ y con src/app/contexts/<contexto>/ (proyectos con la lógica en src/app/).
5
5
  * Probado con dependency-cruiser 16.10.0 y TypeScript 5.6, con un paquete simulado que expone
6
- * @archi-code/ddd/{domain,application,infrastructure,exceptions}. Si la estructura publicada del
6
+ * @archi-code/ddd/{domain,application,infrastructure}. Si la estructura publicada del
7
7
  * paquete difiere, ajusta ARCHI_LAYER.
8
+ * Cada subpath exporta las excepciones de su capa: cada capa importa solo el subpath de su capa y
9
+ * los interiores (domain → /domain; application → /domain y /application; infraestructura → todos).
8
10
  */
9
11
  // Grupo no capturante para app/: así $1 en las reglas es siempre el nombre del contexto.
10
12
  const CTX = '^src/(?:app/)?contexts/([^/]+)/';
@@ -16,7 +18,7 @@ module.exports = {
16
18
  {
17
19
  name: 'domain-solo-paquetes-permitidos',
18
20
  comment:
19
- 'El dominio solo usa @archi-code/ddd (domain y exceptions) y @archi-code/validation; nada más de npm.',
21
+ 'El dominio solo usa @archi-code/ddd/domain (también sus excepciones) y @archi-code/validation; nada más de npm.',
20
22
  severity: 'error',
21
23
  from: { path: CTX + 'domain/' },
22
24
  to: {
@@ -26,7 +28,9 @@ module.exports = {
26
28
  },
27
29
  {
28
30
  name: 'domain-sin-capas-exteriores-del-paquete',
29
- comment: 'El dominio nunca importa @archi-code/ddd/application ni @archi-code/ddd/infrastructure.',
31
+ comment:
32
+ 'El dominio nunca importa @archi-code/ddd/application ni @archi-code/ddd/infrastructure ' +
33
+ '(tampoco sus excepciones).',
30
34
  severity: 'error',
31
35
  from: { path: CTX + 'domain/' },
32
36
  to: { path: ARCHI_LAYER('application|infrastructure') },
@@ -39,7 +43,9 @@ module.exports = {
39
43
  },
40
44
  {
41
45
  name: 'application-sin-infraestructura',
42
- comment: 'La aplicación no conoce adaptadores propios ni @archi-code/ddd/infrastructure.',
46
+ comment:
47
+ 'La aplicación no conoce adaptadores propios ni @archi-code/ddd/infrastructure ' +
48
+ '(tampoco InternalErrorException; un error ajeno lo envuelve UseCase en UnexpectedErrorException).',
43
49
  severity: 'error',
44
50
  from: { path: CTX + 'application/' },
45
51
  to: { path: [CTX + '(?:infrastructure|interfaces)/', ARCHI_LAYER('infrastructure')] },
@@ -47,8 +53,9 @@ module.exports = {
47
53
  {
48
54
  name: 'interfaces-no-saltan-a-infraestructura',
49
55
  comment:
50
- 'Los puntos de entrada llaman a casos de uso; no importan repositorios ni adaptadores. ' +
51
- 'Pueden importar HttpErrorMapper de @archi-code/ddd/infrastructure.',
56
+ 'Los puntos de entrada llaman a casos de uso; no importan repositorios ni adaptadores propios. ' +
57
+ 'De @archi-code/ddd/infrastructure solo usan ExceptionClassifier y ExceptionKind para traducir ' +
58
+ 'los errores (dependency-cruiser no distingue nombres importados: revísalo en la revisión de código).',
52
59
  severity: 'error',
53
60
  from: { path: CTX + 'interfaces/' },
54
61
  to: { path: CTX + 'infrastructure/' },
@@ -32,13 +32,13 @@ Más detalle en `ddd-skills/ddd-event-sourcing-cqrs/GUIA.md`.
32
32
 
33
33
  Contextos que se comunican publicando y consumiendo eventos de dominio. Es la base de la integración robusta descrita en `ddd-skills/ddd-context-mapping/GUIA.md` y del diseño de `ddd-skills/ddd-eventos-dominio/GUIA.md`. Dentro de un contexto, los eventos también desacoplan efectos secundarios del caso de uso: el caso de uso llama a `events.publish(order.pullDomainEvents())` después de `await this.orders.save(order)` y un listener (`PrepareShipmentOnOrderLineAdded`) ejecuta la acción siguiente.
34
34
 
35
- Con el paquete, la tecnología es un `EventChannel` del proyecto (`RabbitMqEventChannel`, un relay que entrega el outbox que el repositorio escribió junto al agregado…) bajo el mismo `DomainEventBus`; en tests, `InMemoryEventChannel`. Hacia otros contextos no viaja `event.toJSON()` de la clase de dominio: el adaptador que habla con el broker lo traduce a un contrato publicado y versionado (`sales.order.OrderPlaced`) [IDDD cap. 13], algo que el paquete no da y escribe el proyecto. `[Fuera de los libros: convención de @archi-code/ddd]`
35
+ Con el paquete, la tecnología es un `EventChannel` del proyecto (`RabbitMqEventChannel`, un relay que entrega el outbox que el repositorio escribió junto al agregado…) bajo el mismo `DomainEventBus`; en tests, `InMemoryEventChannel`. Hacia otros contextos no viaja el evento de dominio tal cual (`{ ...event }`): el adaptador que habla con el broker lo traduce a un contrato publicado y versionado (`sales.order.OrderPlaced`) [IDDD cap. 13], algo que el paquete no da y escribe el proyecto. `[Fuera de los libros: convención de @archi-code/ddd]`
36
36
 
37
37
  ## Event Sourcing
38
38
 
39
39
  El estado de un agregado se guarda como la secuencia de sus eventos y se reconstruye aplicándolos [Destilado cap. 6; IDDD cap. 4]. Aporta historial completo y auditoría, pero cambia por completo la persistencia y las consultas. Ver `ddd-skills/ddd-event-sourcing-cqrs/GUIA.md` antes de decidir.
40
40
 
41
- El paquete no trae event store: cada repositorio guarda el `toPrimitives()` del agregado junto a su `version`, que crece 1 por cada `save`, no por evento. Un agregado con Event Sourcing sigue extendiendo `AggregateRoot` y registrando con `record()`, pero su repositorio (en `infrastructure/`, implementando `AggregateFinder`/`AggregateSaver`) lleva la versión del flujo él mismo y reconstruye el agregado aplicando sus eventos, no con `toPrimitives()`/`fromPrimitives()`. `[Fuera de los libros: convención de @archi-code/ddd]`
41
+ El paquete no trae event store: cada repositorio guarda el `toPrimitives()` del agregado junto a la versión que él mismo lleva (en su `WeakMap`), que crece 1 por cada `save`, no por evento. Un agregado con Event Sourcing sigue extendiendo `AggregateRoot` y registrando con `record()`, pero su repositorio (en `infrastructure/`, implementando `AggregateFinder`/`AggregateSaver`) lleva la versión del flujo él mismo y reconstruye el agregado aplicando sus eventos, no con `toPrimitives()`/`fromPrimitives()`. `[Fuera de los libros: convención de @archi-code/ddd]`
42
42
 
43
43
  ## Tabla de decisión
44
44