@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
@@ -11,10 +11,10 @@ Un caso de uso modifica un solo agregado y lo guarda una vez [IDDD cap. 10]. El
11
11
  Lo que hace el adaptador del repositorio, y nadie más:
12
12
 
13
13
  * Que cada `save` y cada `remove` sea atómico. En MongoDB, un documento por agregado: una sola escritura, atómica por sí misma. Si el agregado ocupa varias tablas SQL, el repositorio abre la transacción de la base de datos **dentro** de `save`/`remove`, con su propia conexión o sesión, y la confirma o la revierte antes de volver.
14
- * La comprobación optimista de la versión: una versión almacenada distinta de `order.version` (o un id ya ocupado al dar de alta) es `ConcurrencyException('Order', order.id.value, order.version)`.
14
+ * La versión de concurrencia optimista, que es solo suya: recuerda la de cada agregado que cargó o guardó en un `WeakMap<Order, number>` privado (`versions`; `0` si no lo conoce, es nuevo). Una versión almacenada distinta de la recordada (o un id ya ocupado al dar de alta) es `ConcurrencyException('Order', order.id.value, version)`.
15
15
  * La conexión, la sesión y el pool.
16
16
  * Envolver los errores del driver en `InternalErrorException` con `{ cause }`.
17
- * `markPersisted()` tras escribir con éxito y, al cargar, `Order.fromPrimitives(row).restoreVersion(version)`.
17
+ * `this.versions.set(order, version + 1)` solo tras escribir con éxito y, al cargar, `Order.fromPrimitives(...)` seguido de `this.versions.set(order, row.version)`. El agregado no declara ni mapea la versión y el caso de uso nunca la ve.
18
18
 
19
19
  ## Varias tablas en PostgreSQL
20
20
 
@@ -55,7 +55,8 @@ En `infrastructure/persistence/postgres-order-repository.ts`:
55
55
 
56
56
  ```ts
57
57
  import type { AggregateFinder, AggregateSaver, PrimitiveTypes } from '@archi-code/ddd/domain';
58
- import { ConcurrencyException, InternalErrorException } from '@archi-code/ddd/exceptions';
58
+ import { ConcurrencyException } from '@archi-code/ddd/application';
59
+ import { InternalErrorException } from '@archi-code/ddd/infrastructure';
59
60
  import { Order, OrderId } from '../../domain/order/order';
60
61
  import type { PgPool } from './pg-pool';
61
62
 
@@ -68,6 +69,8 @@ interface OrderRow {
68
69
  }
69
70
 
70
71
  export class PostgresOrderRepository implements AggregateFinder<Order>, AggregateSaver<Order> {
72
+ private readonly versions = new WeakMap<Order, number>();
73
+
71
74
  constructor(private readonly pool: PgPool) {}
72
75
 
73
76
  async findById(id: OrderId): Promise<Order | null> {
@@ -87,27 +90,33 @@ export class PostgresOrderRepository implements AggregateFinder<Order>, Aggregat
87
90
  [id.value],
88
91
  );
89
92
  const row = rows[0];
90
- return row ? Order.fromPrimitives({ ...row.data, lines: { items: row.items } }).restoreVersion(row.version) : null;
93
+ if (row === undefined) {
94
+ return null;
95
+ }
96
+ const order = Order.fromPrimitives({ ...row.data, lines: { items: row.items } });
97
+ this.versions.set(order, row.version);
98
+ return order;
91
99
  }
92
100
 
93
101
  async save(order: Order): Promise<void> {
102
+ const version = this.versions.get(order) ?? 0;
94
103
  const { lines, ...data } = order.toPrimitives();
95
104
  const client = await this.pool.connect();
96
105
  let broken = false;
97
106
  try {
98
107
  await client.query('BEGIN');
99
108
  const { rowCount } =
100
- order.version === 0
109
+ version === 0
101
110
  ? await client.query(
102
111
  'INSERT INTO orders (id, version, customer_id, status, data) VALUES ($1, 1, $2, $3, $4) ON CONFLICT (id) DO NOTHING',
103
112
  [data.id, data.customerId, data.status, data],
104
113
  )
105
114
  : await client.query(
106
115
  'UPDATE orders SET version = version + 1, status = $3, data = $4 WHERE id = $1 AND version = $2',
107
- [data.id, order.version, data.status, data],
116
+ [data.id, version, data.status, data],
108
117
  );
109
118
  if (rowCount === 0) {
110
- throw new ConcurrencyException('Order', order.id.value, order.version);
119
+ throw new ConcurrencyException('Order', order.id.value, version);
111
120
  }
112
121
  await client.query('DELETE FROM order_lines WHERE order_id = $1', [data.id]);
113
122
  for (const [position, line] of lines.items.entries()) {
@@ -128,14 +137,14 @@ export class PostgresOrderRepository implements AggregateFinder<Order>, Aggregat
128
137
  } finally {
129
138
  client.release(broken);
130
139
  }
131
- order.markPersisted();
140
+ this.versions.set(order, version + 1);
132
141
  }
133
142
  }
134
143
  ```
135
144
 
136
145
  * La transacción empieza y termina dentro de `save`: el caso de uso solo ve una escritura que se hace entera o no se hace.
137
146
  * El `UPDATE` con `version = $2` bloquea la fila del pedido hasta el `COMMIT`; otro `save` del mismo pedido espera y después no casa ninguna fila, así que recibe `ConcurrencyException`.
138
- * `markPersisted()` se llama solo después del `COMMIT`: si la transacción se revierte, el objeto conserva su versión y el caso de uso puede descartarlo y volver a cargarlo.
147
+ * `this.versions.set(order, version + 1)` se hace solo después del `COMMIT`: si la transacción se revierte, el repositorio sigue recordando la versión anterior y el caso de uso puede descartar el objeto y volver a cargarlo.
139
148
  * Si el `ROLLBACK` falla (la conexión se cayó), se relanza el error del trabajo, no el del `ROLLBACK`, y `release(true)` destruye la conexión en vez de devolver al pool una con la transacción a medias.
140
149
  * La lectura es una sola sentencia, así que ve el pedido y sus líneas en el mismo instante.
141
150
  * Un `remove` sigue el mismo patrón: `DELETE FROM orders WHERE id = $1 AND version = $2` dentro de su propia transacción (las líneas caen con `ON DELETE CASCADE`).
@@ -152,7 +161,7 @@ Cuando un evento no puede perderse, el repositorio escribe el agregado y sus eve
152
161
  for (const event of order.pullDomainEvents()) {
153
162
  await client.query('INSERT INTO order_outbox (event_id, body) VALUES ($1, $2)', [
154
163
  event.eventId,
155
- event.toJSON(),
164
+ { ...event },
156
165
  ]);
157
166
  }
158
167
  ```
@@ -164,11 +173,9 @@ El repositorio toma los eventos con `pullDomainEvents()`, así que el `publish`
164
173
  El caso de uso carga, cambia, guarda y publica, sin abrir nada. Si el comando es seguro de repetir contra el estado recargado, la aplicación puede reintentar una o dos veces ante `ConcurrencyException`; si no, el conflicto llega al usuario como 409 [IDDD cap. 10; IDDD apéndice A]. Cada intento vuelve a cargar y vuelve a guardar dentro de `handle`, en un bucle con límite `[Fuera de los libros: convención de @archi-code/ddd]`. Un caso de uso que ejecuta un listener no reintenta por su cuenta: reintenta la reentrega del canal.
165
174
 
166
175
  ```ts
167
- import { UseCase } from '@archi-code/ddd/application';
176
+ import { AggregateNotFoundException, ConcurrencyException, UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
168
177
  import type { Clock, EventBus } from '@archi-code/ddd/application';
169
- import { ValueObjects } from '@archi-code/ddd/domain';
170
178
  import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
171
- import { AggregateNotFoundException, ConcurrencyException } from '@archi-code/ddd/exceptions';
172
179
  import { Order, OrderId, OrderLine } from '../../domain/order/order';
173
180
  import type { OrderEvent } from '../../domain/order/order';
174
181
 
@@ -196,7 +203,7 @@ export class AddOrderLine extends UseCase<AddOrderLineInput, AddOrderLineCommand
196
203
  }
197
204
 
198
205
  protected toCommand(input: AddOrderLineInput): AddOrderLineCommand {
199
- return ValueObjects.build({
206
+ return ValueObjectBuilder.build({
200
207
  orderId: () => new OrderId(input.orderId),
201
208
  line: () =>
202
209
  OrderLine.fromPrimitives({
@@ -8,10 +8,10 @@
8
8
 
9
9
  [Fuera de los libros: convención de @archi-code/ddd]
10
10
 
11
- * Un caso de uso extiende `UseCase<Input, Command, Output>`: un `Input` de primitivos, un `Command` de value objects que construye `toCommand` con `ValueObjects.build` (informa de todos los campos a la vez), y `handle`, sin ayudas de la clase base, carga, cambia, guarda (`await this.orders.save(order)`) y publica (`await this.events.publish(order.pullDomainEvents())`). No hay puerto de transacciones: el caso de uso recibe solo los roles de repositorio que usa (`AggregateFinder`, `AggregateSaver`, `AggregateRemover`), `EventBus`, `Clock` y sus puertos propios, y el repositorio garantiza que cada `save` o `remove` es una escritura atómica. Una acción de negocio por caso de uso, con nombre de verbo del negocio (`PlaceOrder`, `AddOrderLine`).
11
+ * Un caso de uso extiende `UseCase<Input, Command, Output>`: un `Input` de primitivos, un `Command` de value objects que construye `toCommand` con `ValueObjectBuilder.build` (informa de todos los campos a la vez), y `handle`, sin ayudas de la clase base, carga, cambia, guarda (`await this.orders.save(order)`) y publica (`await this.events.publish(order.pullDomainEvents())`). No hay puerto de transacciones: el caso de uso recibe solo los roles de repositorio que usa (`AggregateFinder`, `AggregateSaver`, `AggregateRemover`), `EventBus`, `Clock` y sus puertos propios, y el repositorio garantiza que cada `save` o `remove` es una escritura atómica. Una acción de negocio por caso de uso, con nombre de verbo del negocio (`PlaceOrder`, `AddOrderLine`).
12
12
  * Un caso de uso nunca importa ni llama a otro: las acciones se encadenan con eventos y listeners `<Acción>On<Evento>` decorados con `@ListensTo`, que solo mapean el evento a la entrada de un caso de uso. No hay bus de comandos: el punto de entrada llama a `execute(input)`.
13
13
  * Los servicios de dominio son clases sin base en `domain/` que reciben agregados y value objects, y pueden leer con un contrato `AggregateFinder` o un puerto de dominio; nunca guardan ni publican, y no tocan la persistencia.
14
- * Errores: `AggregateNotFoundException` (404) y `NotAllowedException` (403) en el caso de uso, `DomainException` con código propio en el dominio, y `HttpErrorMapper` en los puntos de entrada; `execute` envuelve cualquier error ajeno al paquete en `InternalErrorException` con `cause`.
14
+ * Errores: `AggregateNotFoundException` (404) y `NotAllowedException` (403) en el caso de uso, `DomainException` con código propio en el dominio, y en los puntos de entrada `ExceptionClassifier.classify(error)`, cuyo `ExceptionKind` cada protocolo traduce a sus códigos; `execute` envuelve cualquier error ajeno al paquete en `UnexpectedErrorException` con `cause`.
15
15
  * Un `ConcurrencyException` se reintenta, si el comando es seguro de repetir, con un bucle en `handle` con un límite de intentos en el que cada intento vuelve a cargar el agregado y vuelve a guardarlo. Un caso de uso que ejecuta un listener no reintenta por sí mismo: reintenta la reentrega del canal.
16
16
  * Un caso de uso que ejecuta un listener para registrar un hecho no lanza si falta el agregado: retorna, para que el canal no lo reentregue sin fin.
17
17
 
@@ -5,11 +5,9 @@
5
5
  Un caso de uso extiende `UseCase<Input, Command, Output>` de `@archi-code/ddd/application`. `execute(input)` llama a `toCommand(input)` y después a `handle(command)`; no hay que escribir `execute`.
6
6
 
7
7
  ```ts
8
- import { ValueObjects } from '@archi-code/ddd/domain';
9
8
  import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
10
- import { UseCase } from '@archi-code/ddd/application';
9
+ import { AggregateNotFoundException, NotAllowedException, UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
11
10
  import type { Clock, EventBus } from '@archi-code/ddd/application';
12
- import { AggregateNotFoundException, NotAllowedException } from '@archi-code/ddd/exceptions';
13
11
 
14
12
  export interface SalesPermissions {
15
13
  canCancelOrders(requester: CustomerId): Promise<boolean>;
@@ -18,7 +16,7 @@ export interface SalesPermissions {
18
16
  export interface CancelOrderInput {
19
17
  orderId: string;
20
18
  requesterId: string;
21
- reason: string;
19
+ reason: string | null;
22
20
  }
23
21
 
24
22
  interface CancelOrderCommand {
@@ -40,10 +38,10 @@ export class CancelOrder extends UseCase<CancelOrderInput, CancelOrderCommand, v
40
38
  }
41
39
 
42
40
  protected toCommand(input: CancelOrderInput): CancelOrderCommand {
43
- return ValueObjects.build({
41
+ return ValueObjectBuilder.build({
44
42
  orderId: () => new OrderId(input.orderId),
45
43
  requester: () => new CustomerId(input.requesterId),
46
- reason: () => new CancellationReason(input.reason),
44
+ reason: () => new CancellationReason(input.reason as string),
47
45
  });
48
46
  }
49
47
 
@@ -72,7 +70,7 @@ Autorizar, cargar, invocar el dominio, guardar y publicar, y ninguna regla de ne
72
70
  ## Entrada
73
71
 
74
72
  * Primitivos o DTO o comando [PHP: Application, citando a Vernon]. Con el paquete, dos tipos por caso de uso `[Fuera de los libros: convención de @archi-code/ddd]`: el `Input`, plano y de primitivos (se puede serializar y encolar si un día hace falta [PHP: Application]), y el `Command`, de value objects, que construye `toCommand`.
75
- * La conversión a value objects ocurre en `toCommand` con `ValueObjects.build`: si algún campo no es válido lanza una sola `ValidationException` con el `field` y el `code` de cada problema (`ValueObjects.buildList` nombra los elementos de una lista por posición, `lines.1.quantity`), y `handle` no llega a ejecutarse.
73
+ * La conversión a value objects ocurre en `toCommand` con `ValueObjectBuilder.build`: si algún campo no es válido lanza una sola `ValidationException` con el `field` y el `code` de cada problema (`ValueObjectBuilder.buildList` nombra los elementos de una lista por posición, `lines.1.quantity`), y `handle` no llega a ejecutarse.
76
74
  * El adaptador de entrada solo comprueba la forma de la petición (que un campo sea texto); las reglas de cada valor son de los value objects.
77
75
  * Nunca recibas el agregado desde el controlador.
78
76
 
@@ -93,7 +91,7 @@ Para pantallas, mejor una consulta de lectura aparte que devuelve un DTO sin pas
93
91
 
94
92
  ## Command bus
95
93
 
96
- *DDD in PHP* presenta el bus de comandos (Tactician) para enlazar cada comando con su manejador; los objetos request se parecen ya a comandos [PHP: Application]. El paquete no trae bus de comandos `[Fuera de los libros: convención de @archi-code/ddd]`: el composition root construye cada caso de uso y se lo pasa al punto de entrada, que llama a `execute(input)`. Lo común a todos los casos de uso ya está en `UseCase.execute` (dejar pasar las excepciones del paquete y envolver cualquier otro error en `InternalErrorException` con `cause`), y la atomicidad de cada escritura es del repositorio.
94
+ *DDD in PHP* presenta el bus de comandos (Tactician) para enlazar cada comando con su manejador; los objetos request se parecen ya a comandos [PHP: Application]. El paquete no trae bus de comandos `[Fuera de los libros: convención de @archi-code/ddd]`: el composition root construye cada caso de uso y se lo pasa al punto de entrada, que llama a `execute(input)`. Lo común a todos los casos de uso ya está en `UseCase.execute` (dejar pasar las excepciones del paquete y envolver cualquier otro error en `UnexpectedErrorException` con `cause`), y la atomicidad de cada escritura es del repositorio.
97
95
 
98
96
  ## Eventos en la capa de aplicación
99
97
 
@@ -105,7 +103,8 @@ Para pantallas, mejor una consulta de lectura aparte que devuelve un DTO sin pas
105
103
  Cada repositorio comprueba la versión del agregado y lanza `ConcurrencyException` (409) si otro lo cambió después de cargarlo. Si el comando es seguro de repetir sobre el nuevo estado, el caso de uso puede reintentarlo [IDDD cap. 10; IDDD apéndice A]: un bucle en `handle` en el que cada intento vuelve a cargar el agregado y vuelve a guardarlo; pocos intentos y después se relanza. Un caso de uso que ejecuta un listener no reintenta por sí mismo: reintenta la reentrega del canal.
106
104
 
107
105
  ```ts
108
- import { ConcurrencyException, DomainException } from '@archi-code/ddd/exceptions';
106
+ import { DomainException } from '@archi-code/ddd/domain';
107
+ import { ConcurrencyException } from '@archi-code/ddd/application';
109
108
 
110
109
  export interface ProductPricing {
111
110
  priceForSale(sku: Sku): Promise<Money | null>;
@@ -130,7 +129,7 @@ export class AddOrderLine extends UseCase<AddOrderLineInput, AddOrderLineCommand
130
129
  }
131
130
 
132
131
  protected toCommand(input: AddOrderLineInput): AddOrderLineCommand {
133
- return ValueObjects.build({
132
+ return ValueObjectBuilder.build({
134
133
  orderId: () => new OrderId(input.orderId),
135
134
  sku: () => new Sku(input.sku),
136
135
  quantity: () => new Quantity(input.quantity),
@@ -167,13 +166,14 @@ export class AddOrderLine extends UseCase<AddOrderLineInput, AddOrderLineCommand
167
166
 
168
167
  ## Errores
169
168
 
170
- * El dominio lanza `DomainException` con nombre y código propio (`class OrderCannotBeCancelled extends DomainException`, `super(message, ['ORD002'])`); los value objects y `ValueObjects.build` lanzan `ValidationException`.
169
+ * El dominio lanza `DomainException` con nombre y código propio (`class OrderCannotBeCancelled extends DomainException`, `super(message, ['ORD002'])`); los value objects y `ValueObjectBuilder.build` lanzan `ValidationException`.
171
170
  * La aplicación añade las suyas: `AggregateNotFoundException('Order', id)` cuando `findById` devuelve `null`, `NotAllowedException(message)` cuando el solicitante no puede ejecutar la acción, `ApplicationException` para otros errores de aplicación. Al traducir otro error, siempre con `{ cause: error }`.
172
- * Los errores se dejan propagar: no se capturan en `handle` para relanzarlos; `execute` deja pasar las excepciones del paquete y envuelve el resto en `InternalErrorException`.
173
- * El punto de entrada los traduce con `HttpErrorMapper` de `@archi-code/ddd/infrastructure`: 404 `AggregateNotFoundException`, 403 `NotAllowedException`, 409 `ConcurrencyException`, 422 `ValidationException` y cualquier otra `DomainException` (regla de negocio), 400 otros errores de aplicación y 500, con mensaje genérico, para lo demás. Los códigos propios se ajustan por código (`new HttpErrorMapper({ statusByCode: { ORD001: 409 } })`) `[Fuera de los libros: convención de @archi-code/ddd]`.
171
+ * Los errores se dejan propagar: no se capturan en `handle` para relanzarlos; `execute` deja pasar las excepciones del paquete y envuelve el resto en `UnexpectedErrorException` (de `@archi-code/ddd/application`).
172
+ * El punto de entrada los clasifica con `ExceptionClassifier.classify(error)` de `@archi-code/ddd/infrastructure`, que no sabe de ningún protocolo: `NotFound` (`AggregateNotFoundException`), `NotAllowed` (`NotAllowedException`), `Conflict` (`ConcurrencyException`), `Malformed` (`TypePrimitiveException`, `RequiredValueException`), `Invalid` (`ValidationException` y cualquier otra `DomainException`), `Rejected` (otros errores de aplicación) e `Internal` (`UnexpectedErrorException`, `InvalidEventDefinitionException` y lo demás). Cada protocolo traduce el `ExceptionKind` a sus códigos (en HTTP: 404, 403, 409, 400, 422, 400 y 500), usa `error.code` para los códigos propios (`ORD001` → 409), oculta el mensaje de `Internal`, devuelve los `errors` de una `ValidationException` y registra él mismo los fallos internos `[Fuera de los libros: convención de @archi-code/ddd]`. Un controlador HTTP usa el `toHttpError` del proyecto (en `interfaces/http/`; completo en `ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md`):
174
173
 
175
174
  ```ts
176
- import type { HttpErrorMapper } from '@archi-code/ddd/infrastructure';
175
+ import type { Logger } from '@archi-code/ddd/application';
176
+ import { toHttpError } from './httpError';
177
177
 
178
178
  interface HttpRequest {
179
179
  params: { orderId: string };
@@ -189,7 +189,7 @@ interface HttpResponse {
189
189
  export class CancelOrderController {
190
190
  constructor(
191
191
  private readonly cancelOrder: CancelOrder,
192
- private readonly errors: HttpErrorMapper,
192
+ private readonly logger: Logger,
193
193
  ) {}
194
194
 
195
195
  async cancel(request: HttpRequest): Promise<HttpResponse> {
@@ -197,11 +197,11 @@ export class CancelOrderController {
197
197
  await this.cancelOrder.execute({
198
198
  orderId: request.params.orderId,
199
199
  requesterId: request.auth.customerId,
200
- reason: typeof request.body.reason === 'string' ? request.body.reason : '',
200
+ reason: typeof request.body.reason === 'string' ? request.body.reason : null,
201
201
  });
202
202
  return { status: 204 };
203
203
  } catch (error) {
204
- return this.errors.map(error);
204
+ return toHttpError(error, this.logger);
205
205
  }
206
206
  }
207
207
  }
@@ -215,7 +215,7 @@ Con adaptadores en memoria: `InMemoryOrderRepository`, que el proyecto escribe e
215
215
 
216
216
  ```ts
217
217
  import { describe, expect, it } from 'vitest';
218
- import { NotAllowedException } from '@archi-code/ddd/exceptions';
218
+ import { NotAllowedException } from '@archi-code/ddd/application';
219
219
  import {
220
220
  DomainEventBus,
221
221
  EventRegistry,
@@ -245,7 +245,7 @@ describe('Cancelling an order', () => {
245
245
  await cancelOrder.execute({ orderId, requesterId: laura, reason: 'Wrong size' });
246
246
 
247
247
  expect(channel.sent).toMatchObject([
248
- { eventName: 'OrderCancelled', aggregateId: orderId, reason: 'Wrong size' },
248
+ { eventName: 'OrderCancelled', orderId, reason: 'Wrong size' },
249
249
  ]);
250
250
  });
251
251
 
@@ -33,9 +33,8 @@ total(): Money;
33
33
  Lo que añade Ventas en esta guía:
34
34
 
35
35
  ```ts
36
- import { AggregateRoot, BooleanType, Required, Specification, StringType } from '@archi-code/ddd/domain';
36
+ import { AggregateRoot, BooleanType, DomainException, Required, Specification, StringType } from '@archi-code/ddd/domain';
37
37
  import type { PrimitiveTypes } from '@archi-code/ddd/domain';
38
- import { DomainException } from '@archi-code/ddd/exceptions';
39
38
 
40
39
  export class OrderAbove extends Specification<Order> {
41
40
  constructor(private readonly minimum: Money) {
@@ -123,11 +122,9 @@ export class OrderCancellation {
123
122
  `ProductPricing` y `ProductNotSellable` son los de `ddd-skills/ddd-servicios-capa-aplicacion/references/capa-aplicacion-ts.md`.
124
123
 
125
124
  ```ts
126
- import { ValueObjects } from '@archi-code/ddd/domain';
127
125
  import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
128
- import { UseCase } from '@archi-code/ddd/application';
126
+ import { AggregateNotFoundException, UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
129
127
  import type { Clock, EventBus } from '@archi-code/ddd/application';
130
- import { AggregateNotFoundException } from '@archi-code/ddd/exceptions';
131
128
 
132
129
  export interface PlaceOrderInput {
133
130
  customerId: string;
@@ -153,11 +150,11 @@ export class PlaceOrder extends UseCase<PlaceOrderInput, PlaceOrderCommand, stri
153
150
  }
154
151
 
155
152
  protected toCommand(input: PlaceOrderInput): PlaceOrderCommand {
156
- return ValueObjects.build({
153
+ return ValueObjectBuilder.build({
157
154
  customerId: () => new CustomerId(input.customerId),
158
155
  lines: () =>
159
- ValueObjects.buildList(input.lines, (line) =>
160
- ValueObjects.build({
156
+ ValueObjectBuilder.buildList(input.lines, (line) =>
157
+ ValueObjectBuilder.build({
161
158
  sku: () => new Sku(line.sku),
162
159
  quantity: () => new Quantity(line.quantity),
163
160
  }),
@@ -206,20 +203,20 @@ Los gastos de envío se han dejado fuera de este caso de uso para mantenerlo ali
206
203
  Una reacción a un evento es un listener `<Acción>On<Evento>`, decorado con `@ListensTo` y con un nombre de consumidor estable, que solo traduce el evento a la entrada de un caso de uso. Ese caso de uso registra un hecho que ya ocurrió: si el pedido no existe, retorna en vez de lanzar (el canal lo reentregaría sin fin), y `registerStockReservation` es idempotente, porque la entrega es al menos una vez.
207
204
 
208
205
  ```ts
209
- import { EventBase, ValueObjects } from '@archi-code/ddd/domain';
206
+ import { EventBase } from '@archi-code/ddd/domain';
210
207
  import type { AggregateFinder, AggregateSaver, EventMetadata } from '@archi-code/ddd/domain';
211
- import { EventListener, ListensTo, UseCase } from '@archi-code/ddd/application';
208
+ import { EventListener, ListensTo, UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
212
209
  import type { Clock, EventBus } from '@archi-code/ddd/application';
213
210
 
214
211
  export class StockReserved extends EventBase {
215
212
  static readonly eventName = 'StockReserved';
216
213
 
217
214
  constructor(
218
- reservationId: string,
215
+ readonly reservationId: string,
219
216
  readonly orderId: string,
220
217
  metadata?: EventMetadata,
221
218
  ) {
222
- super(reservationId, metadata);
219
+ super(metadata);
223
220
  }
224
221
  }
225
222
 
@@ -247,7 +244,7 @@ export class RegisterReservation extends UseCase<
247
244
  }
248
245
 
249
246
  protected toCommand(input: RegisterReservationInput): RegisterReservationCommand {
250
- return ValueObjects.build({
247
+ return ValueObjectBuilder.build({
251
248
  orderId: () => new OrderId(input.orderId),
252
249
  reservationId: () => new ReservationId(input.reservationId),
253
250
  });
@@ -273,7 +270,7 @@ export class RegisterReservationOnStockReserved extends EventListener<StockReser
273
270
  async handle(event: StockReserved): Promise<void> {
274
271
  await this.registerReservation.execute({
275
272
  orderId: event.orderId,
276
- reservationId: event.aggregateId,
273
+ reservationId: event.reservationId,
277
274
  });
278
275
  }
279
276
  }
@@ -301,7 +298,7 @@ describe('Registering the stock reservation of an order', () => {
301
298
  const orders = new InMemoryOrderRepository();
302
299
  const registry = new EventRegistry<StockReserved>().register(
303
300
  StockReserved,
304
- (json) => new StockReserved(json.aggregateId, json.orderId as string, json),
301
+ (json) => new StockReserved(json.reservationId as string, json.orderId as string, json),
305
302
  );
306
303
  const channel = new InMemoryEventChannel();
307
304
  const bus = new DomainEventBus<StockReserved>(channel, registry);
@@ -14,9 +14,8 @@ Ejemplo de IDDD: autenticar a un usuario solo si su tenant está activo. Ponerlo
14
14
  El paquete no trae una clase base para servicios de dominio: son clases normales en `domain/`, con el nombre de la operación de negocio. Reciben agregados ya cargados y value objects, y devuelven un value object.
15
15
 
16
16
  ```ts
17
- import { Composite, Required, StringType } from '@archi-code/ddd/domain';
17
+ import { Composite, DomainException, Required, StringType } from '@archi-code/ddd/domain';
18
18
  import type { SatisfiedBy } from '@archi-code/ddd/domain';
19
- import { DomainException } from '@archi-code/ddd/exceptions';
20
19
  import { Validations } from '@archi-code/validation';
21
20
 
22
21
  @Validations([{ validator: 'isIn', properties: { values: ['PENINSULA', 'BALEARES', 'CANARIAS'] } }])
@@ -79,7 +78,7 @@ export interface ExchangeRates {
79
78
  ```
80
79
 
81
80
  ```ts
82
- import { InternalErrorException } from '@archi-code/ddd/exceptions';
81
+ import { InternalErrorException } from '@archi-code/ddd/infrastructure';
83
82
 
84
83
  interface EcbClient {
85
84
  rate(from: string, to: string, day: string): Promise<number>;
@@ -104,11 +103,11 @@ export class EcbExchangeRates implements ExchangeRates {
104
103
 
105
104
  Un servicio de dominio es buen sitio para validar que varios agregados sean coherentes entre sí [IDDD cap. 5]. Recibe los agregados que el caso de uso ya cargó y comprueba. Si detecta un problema, informa (error o evento); no corrige varios agregados a la vez: cada agregado se guarda en su propia escritura atómica.
106
105
 
107
- Para informar de todos los problemas a la vez, reúne `ValidationIssue`s y lanza una sola `ValidationException` (HTTP 422 con `HttpErrorMapper`, cada problema con su `field` y su `code`); si basta con uno, lanza una `DomainException` con nombre y código propio:
106
+ Para informar de todos los problemas a la vez, reúne `ValidationIssue`s y lanza una sola `ValidationException` (`ExceptionKind.Invalid`, 422 en HTTP, cada problema con su `field` y su `code`); si basta con uno, lanza una `DomainException` con nombre y código propio:
108
107
 
109
108
  ```ts
110
- import { ValidationException } from '@archi-code/ddd/exceptions';
111
- import type { ValidationIssue } from '@archi-code/ddd/exceptions';
109
+ import { ValidationException } from '@archi-code/ddd/domain';
110
+ import type { ValidationIssue } from '@archi-code/ddd/domain';
112
111
 
113
112
  export class ShipmentCheck {
114
113
  assertCanShip(order: Order, customer: Customer): void {
@@ -11,7 +11,7 @@
11
11
  * Un value object es una subclase con nombre de un tipo del paquete (`class Email extends Required(StringType) {}`) con sus reglas en `@Validations` (de `@archi-code/validation`): se crea con `new` y su constructor lo deja siempre válido; no hay factorías `of()` ni `assertValid()`, y la normalización va en `filter`.
12
12
  * Un valor de varios atributos (`Money`, `DateRange`, `OrderLine`, `OrderLines`) es `Composite({...})`, con las reglas que combinan campos como `custom`; un estado, un `EnumType`; un id, `IdType`, generado por el propio value object (`OrderId.generate()`), nunca por el repositorio.
13
13
  * `Money` va en céntimos enteros (`Cents`, `Required(NumberType)` con `isInt`) en lugar de `bigint`: exacto hasta `Number.MAX_SAFE_INTEGER` (9.007.199.254.740.991 céntimos); el paquete no tiene tipo `bigint`.
14
- * Los errores de una entrada se recogen con `ValueObjects.build({...})` y `ValidationException.errors`, no con un manejador de notificación.
14
+ * Los errores de una entrada se recogen con `ValueObjectBuilder.build({...})` y `ValidationException.errors`, no con un manejador de notificación.
15
15
  * Un value object es opcional por defecto (`value: T | null`) y `Required(XType)` lo hace obligatorio. Los libros crean siempre el valor con contenido y modelan lo opcional en el campo (`reservation: ReservationId | null`); con el paquete valen las dos formas.
16
16
  * La identidad de una entidad o raíz siempre es un value object obligatorio (`IdType` o `Required(...)`): un id sin valor lanza `RequiredValueException`, así dos entidades nuevas nunca son iguales por un id vacío [IDDD cap. 5].
17
17
  * Todo el estado de un agregado es value objects (también un booleano), cada campo guardado tiene un getter público con su nombre sin `_` y los valores derivados son métodos, nunca getters. La reconstitución es `static fromPrimitives(PrimitiveTypes<X>)` con su `toPrimitives()`, que el repositorio llama directamente y cuyo viaje de ida y vuelta prueba el spec del agregado (nada lo comprueba en ejecución). Los libros solo piden getters donde hacen falta y un mapper en infraestructura.
@@ -86,7 +86,7 @@ Recomendación por defecto: identidad generada por la aplicación, **temprana**
86
86
  2. **Objeto completo:** reglas que combinan atributos (las de un valor de varios atributos, reglas `custom` de su `Composite`). IDDD sugiere un validador separado cuando esa validación cambia más que la entidad.
87
87
  3. **Composición:** reglas sobre varios objetos o agregados. Un servicio de dominio que recibe lo cargado o lee lo necesario con `AggregateFinder`.
88
88
 
89
- Detalle, cómo acumular todos los errores (`ValueObjects.build` en lugar del patrón de notificación) y qué validar en la interfaz frente al dominio en `ddd-skills/ddd-value-objects-entidades/references/validacion.md`.
89
+ Detalle, cómo acumular todos los errores (`ValueObjectBuilder.build` en lugar del patrón de notificación) y qué validar en la interfaz frente al dominio en `ddd-skills/ddd-value-objects-entidades/references/validacion.md`.
90
90
 
91
91
  ## Paso 5. Diseño flexible y Specification
92
92
 
@@ -94,7 +94,7 @@ Revisa el diseño con los patrones de diseño flexible (Supple Design): interfac
94
94
 
95
95
  ## Paso 6. Persistencia sin fugas
96
96
 
97
- Cómo guardar value objects (columnas embebidas, JSON, tabla propia para colecciones) sin convertirlos en entidades del modelo: `ddd-skills/ddd-value-objects-entidades/references/persistencia-valores.md`. La regla: diseña el modelo de datos al servicio del modelo de dominio [IDDD cap. 6]. Con el paquete, el repositorio parte de `order.toPrimitives()` (y carga con `Order.fromPrimitives(row).restoreVersion(version)`) y decide en infraestructura cómo reparte los primitivos en columnas.
97
+ Cómo guardar value objects (columnas embebidas, JSON, tabla propia para colecciones) sin convertirlos en entidades del modelo: `ddd-skills/ddd-value-objects-entidades/references/persistencia-valores.md`. La regla: diseña el modelo de datos al servicio del modelo de dominio [IDDD cap. 6]. Con el paquete, el repositorio parte de `order.toPrimitives()` (y carga con `Order.fromPrimitives(...)`, recordando la versión de la fila en su propio `WeakMap`, nunca en el agregado) y decide en infraestructura cómo reparte los primitivos en columnas.
98
98
 
99
99
  ## Señales de alarma
100
100
 
@@ -43,7 +43,7 @@ const order = Order.place(OrderId.generate(), customerId, lines, shippingAddress
43
43
 
44
44
  ### Identidad sustituta
45
45
 
46
- Si el ORM o la base exige su propia clave (un autoincremental), usa dos identidades: la del dominio y una sustituta para la persistencia, oculta al modelo y a sus clientes [IDDD cap. 5]. Con el paquete, la sustituta vive solo en el repositorio: se guarda junto a los primitivos (como la `version`), nunca dentro de `toPrimitives()` ni como campo del agregado.
46
+ Si el ORM o la base exige su propia clave (un autoincremental), usa dos identidades: la del dominio y una sustituta para la persistencia, oculta al modelo y a sus clientes [IDDD cap. 5]. Con el paquete, la sustituta vive solo en el repositorio: se guarda junto a los primitivos (como la `version`, que también es solo del repositorio), nunca dentro de `toPrimitives()` ni como campo del agregado.
47
47
 
48
48
  ### Estabilidad
49
49
 
@@ -51,13 +51,12 @@ La identidad no cambia durante la vida de la entidad. IDDD la protege con un set
51
51
 
52
52
  ## Patrón de implementación
53
53
 
54
- No se escribe una clase base `Entity`: viene de `@archi-code/ddd/domain`. `Entity<Id>` solo guarda `readonly id` y compara por clase e id; `AggregateRoot<Id, Event>` la extiende y añade eventos (`record()`, `pullDomainEvents()`) y la `version` para concurrencia optimista, que solo tocan los repositorios.
54
+ No se escribe una clase base `Entity`: viene de `@archi-code/ddd/domain`. `Entity<Id>` solo guarda `readonly id` y compara por clase e id; `AggregateRoot<Id, Event>` la extiende y añade solo los eventos (`record()`, `pullDomainEvents()`); la versión para concurrencia optimista no está en el agregado, la recuerda cada repositorio en un `WeakMap<Order, number>` `[Fuera de los libros: convención de @archi-code/ddd]`.
55
55
 
56
56
  Una raíz de agregado extiende `AggregateRoot`. Una entidad hija (una parte con identidad propia dentro del agregado, que se busca por ese id) extiende `Entity`, guarda su id local y value objects, y sus métodos de negocio solo los llama su raíz [IDDD cap. 5 y cap. 10]:
57
57
 
58
58
  ```ts
59
- import { BooleanType, Entity, NumberType, Required, StringType } from '@archi-code/ddd/domain';
60
- import { DomainException } from '@archi-code/ddd/exceptions';
59
+ import { BooleanType, DomainException, Entity, NumberType, Required, StringType } from '@archi-code/ddd/domain';
61
60
 
62
61
  export class SeatNumber extends Required(NumberType) {}
63
62
  export class SeatLabel extends Required(StringType) {}
@@ -97,9 +96,8 @@ Separar dos caminos evita que la carga desde base de datos vuelva a disparar reg
97
96
 
98
97
  ```ts
99
98
  import { Validations } from '@archi-code/validation';
100
- import { AggregateRoot, EnumType, EventBase, Required, StringType } from '@archi-code/ddd/domain';
99
+ import { AggregateRoot, DomainException, EnumType, EventBase, Required, StringType } from '@archi-code/ddd/domain';
101
100
  import type { EventMetadata, PrimitiveTypes } from '@archi-code/ddd/domain';
102
- import { DomainException } from '@archi-code/ddd/exceptions';
103
101
 
104
102
  export class CustomerName extends Required(StringType) {}
105
103
 
@@ -127,11 +125,11 @@ export class CustomerBlocked extends EventBase {
127
125
  static readonly eventName = 'CustomerBlocked';
128
126
 
129
127
  constructor(
130
- customerId: string,
128
+ readonly customerId: string,
131
129
  readonly reason: string,
132
130
  metadata?: EventMetadata,
133
131
  ) {
134
- super(customerId, metadata);
132
+ super(metadata);
135
133
  }
136
134
  }
137
135
 
@@ -209,7 +207,7 @@ Fíjate en:
209
207
  * Ningún setter; cada cambio es un comportamiento con nombre y sus reglas. `block` es idempotente y registra `CustomerBlocked` con el reloj que recibe, nunca con `new Date()`.
210
208
  * Todos los atributos son value objects; cambiar el correo es reemplazar un `Email`.
211
209
  * Getters solo para lo que los clientes necesitan leer, dicen los libros. Con el paquete, cada campo que se guarda tiene un getter público con su nombre sin `_`, porque `toPrimitives()` los lee `[Fuera de los libros: convención de @archi-code/ddd]`. Los valores derivados son métodos (`isBlocked()`), nunca getters.
212
- * El agregado no declara `version` ni la incluye en `toPrimitives()`: la lleva la base y la gestionan los repositorios.
210
+ * El agregado no declara `version` ni la incluye en `toPrimitives()`: la versión es solo del repositorio, que la recuerda por agregado en un `WeakMap` y la guarda junto a los primitivos.
213
211
 
214
212
  ## Seguimiento de cambios
215
213
 
@@ -27,7 +27,7 @@ Antes de convertir un valor en entidad "porque tiene tabla", responde [IDDD cap.
27
27
  ## En TypeScript
28
28
 
29
29
  `[Fuera de los libros: convención de @archi-code/ddd]`
30
- * Nada de mappers escritos a mano ni decoradores de ORM en las clases de dominio. El agregado da sus datos planos con `toPrimitives()` y se rehace con `static fromPrimitives()`; el repositorio solo llama a `order.toPrimitives()` y `Order.fromPrimitives(row).restoreVersion(version)`. Nada comprueba en ejecución que no se pierda ningún campo privado: lo prueba el spec del agregado con el viaje de ida y vuelta (`Order.fromPrimitives(order.toPrimitives()).toPrimitives()` igual a `order.toPrimitives()`).
30
+ * Nada de mappers escritos a mano ni decoradores de ORM en las clases de dominio. El agregado da sus datos planos con `toPrimitives()` y se rehace con `static fromPrimitives()`; el repositorio solo llama a `order.toPrimitives()` y `Order.fromPrimitives(...)`, y la versión de concurrencia la recuerda él mismo en un `WeakMap<Order, number>`, nunca el agregado. Nada comprueba en ejecución que no se pierda ningún campo privado: lo prueba el spec del agregado con el viaje de ida y vuelta (`Order.fromPrimitives(order.toPrimitives()).toPrimitives()` igual a `order.toPrimitives()`).
31
31
  * Los primitivos ya dan la forma de cada opción de la tabla: un value object simple es su valor, un `Composite` es un objeto (`shippingAddress: { street, city, country }`), una lista de composites es una lista de objetos (`lines: { items: [...] }`) y un `EnumType` es la opción del enum (`status: 'pending'`).
32
32
  * Con un almacén de documentos (MongoDB), los primitivos se guardan tal cual. Con tablas, el repositorio decide cómo se reparten en columnas: el formato de la fila sigue en infraestructura y no deforma el modelo.
33
33
 
@@ -49,7 +49,7 @@ interface OrderRow {
49
49
  version: number;
50
50
  }
51
51
 
52
- function rowOf(order: Order): OrderRow {
52
+ function rowOf(order: Order, version: number): OrderRow {
53
53
  const primitives = order.toPrimitives();
54
54
  return {
55
55
  id: primitives.id,
@@ -61,7 +61,7 @@ function rowOf(order: Order): OrderRow {
61
61
  status: primitives.status,
62
62
  stock_reservation: primitives.stockReservation,
63
63
  payment_authorization: primitives.paymentAuthorization,
64
- version: order.version + 1,
64
+ version: version + 1,
65
65
  };
66
66
  }
67
67
 
@@ -78,11 +78,11 @@ function orderOf(row: OrderRow): Order {
78
78
  status: row.status as Status,
79
79
  stockReservation: row.stock_reservation,
80
80
  paymentAuthorization: row.payment_authorization,
81
- }).restoreVersion(row.version);
81
+ });
82
82
  }
83
83
  ```
84
84
 
85
- `Order`, `ShippingAddress` y `Status` son los de `ddd-skills/ddd-agregados/references/implementacion-ts.md`. La dirección va en columnas de la fila del padre, las líneas y la autorización de pago en columnas JSON (`jsonb` en PostgreSQL) y el estado como su código. Con el driver `pg`, una columna `jsonb` se lee ya como objeto (la fecha de caducidad de la autorización llega como texto ISO, que `ExpiresAt` convierte), y al escribir el parámetro se pasa como `JSON.stringify(row.lines)` (un array sin convertir lo enviaría como array de PostgreSQL). La `version` se guarda junto a los primitivos, nunca dentro; la consulta con la condición de versión y `markPersisted()` están en `ddd-skills/ddd-repositorios-persistencia/GUIA.md`.
85
+ `Order`, `ShippingAddress` y `Status` son los de `ddd-skills/ddd-agregados/references/implementacion-ts.md`. La dirección va en columnas de la fila del padre, las líneas y la autorización de pago en columnas JSON (`jsonb` en PostgreSQL) y el estado como su código. Con el driver `pg`, una columna `jsonb` se lee ya como objeto (la fecha de caducidad de la autorización llega como texto ISO, que `ExpiresAt` convierte), y al escribir el parámetro se pasa como `JSON.stringify(row.lines)` (un array sin convertir lo enviaría como array de PostgreSQL). La `version` se guarda junto a los primitivos, nunca dentro: `rowOf` recibe la que el repositorio recuerda para ese agregado en su `WeakMap` (`0` si es nuevo) y, tras cargar con `orderOf`, el repositorio anota `row.version` en ese `WeakMap`; la consulta con la condición de versión y cuándo se actualiza el `WeakMap` están en `ddd-skills/ddd-repositorios-persistencia/GUIA.md`.
86
86
 
87
87
  * Al cargar, `fromPrimitives` rehace cada value object con su constructor, que valida: un dato corrupto falla al cargar (`ValidationException`) en vez de propagarse. No se repiten reglas de creación ni se registran eventos.
88
88
  * Con Prisma o Drizzle, el esquema es de infraestructura y el repositorio sigue pasando por `order.toPrimitives()` y `Order.fromPrimitives()`.
@@ -24,15 +24,15 @@ Criterio práctico `[Fuera de los libros]`:
24
24
 
25
25
  Para devolver todos los errores en vez de parar en el primero, IDDD y *DDD in PHP* usan un manejador de notificación que recibe los fallos [IDDD cap. 5; PHP: Entities]. Con el paquete no se escribe ese manejador `[Fuera de los libros: convención de @archi-code/ddd]`:
26
26
 
27
- * **Entrada de un caso de uso:** `ValueObjects.build({...})` ejecuta todas las factorías y lanza una sola `ValidationException` cuyos `errors` (`ValidationIssue`: `{ field, code, message }`) llevan el campo y el código de cada problema; `ValueObjects.buildList` nombra los elementos de una lista por su posición (`lines.1.quantity`).
27
+ * **Entrada de un caso de uso:** `ValueObjectBuilder.build({...})` ejecuta todas las factorías y lanza una sola `ValidationException` cuyos `errors` (`ValidationIssue`: `{ field, code, message }`) llevan el campo y el código de cada problema; `ValueObjectBuilder.buildList` nombra los elementos de una lista por su posición (`lines.1.quantity`).
28
28
 
29
29
  ```ts
30
- import { ValueObjects } from '@archi-code/ddd/domain';
30
+ import { ValueObjectBuilder } from '@archi-code/ddd/application';
31
31
 
32
- const command = ValueObjects.build({
32
+ const command = ValueObjectBuilder.build({
33
33
  customerId: () => new CustomerId(input.customerId),
34
34
  email: () => new Email(input.email),
35
- lines: () => ValueObjects.buildList(input.lines, (line) => OrderLine.fromPrimitives(line)),
35
+ lines: () => ValueObjectBuilder.buildList(input.lines, (line) => OrderLine.fromPrimitives(line)),
36
36
  });
37
37
  // input { customerId: 'x', email: 'ana', lines: [...] } → una ValidationException:
38
38
  // errors: [
@@ -44,8 +44,8 @@ const command = ValueObjects.build({
44
44
  * **Un objeto ya construido** (la ficha de un producto que se quiere publicar, en el contexto de Catálogo): un servicio de dominio recoge los `ValidationIssue` y lanza una sola `new ValidationException(issues)`.
45
45
 
46
46
  ```ts
47
- import { ValidationException } from '@archi-code/ddd/exceptions';
48
- import type { ValidationIssue } from '@archi-code/ddd/exceptions';
47
+ import { ValidationException } from '@archi-code/ddd/domain';
48
+ import type { ValidationIssue } from '@archi-code/ddd/domain';
49
49
 
50
50
  export class ListingReadyToPublish {
51
51
  check(listing: ProductListing): void {
@@ -80,11 +80,11 @@ Un agregado puede estar en un estado intermedio legítimo en el que no procede v
80
80
  ## Errores con nombre
81
81
 
82
82
  `[Fuera de los libros]`
83
- * Una clase de error por regla de negocio (`OrderMustHaveLines`, `CurrencyMismatch`), con un código estable para traducir a mensajes de usuario y a códigos HTTP en el adaptador. Con el paquete, una subclase de `DomainException` (de `@archi-code/ddd/exceptions`) con su código en el segundo argumento.
83
+ * Una clase de error por regla de negocio (`OrderMustHaveLines`, `CurrencyMismatch`), con un código estable para traducir a mensajes de usuario y a códigos HTTP en el adaptador. Con el paquete, una subclase de `DomainException` (de `@archi-code/ddd/domain`) con su código en el segundo argumento.
84
84
  * No uses `Error('invalid')` en el dominio.
85
85
 
86
86
  ```ts
87
- import { DomainException } from '@archi-code/ddd/exceptions';
87
+ import { DomainException } from '@archi-code/ddd/domain';
88
88
 
89
89
  export class OrderMustHaveLines extends DomainException {
90
90
  constructor() {
@@ -93,4 +93,4 @@ export class OrderMustHaveLines extends DomainException {
93
93
  }
94
94
  ```
95
95
 
96
- `error.code` es `'ORD002'`. En la entrada, `HttpErrorMapper` (de `@archi-code/ddd/infrastructure`) traduce sin tablas escritas a mano: 422 para una `ValidationException` o cualquier `DomainException`, 400 para un error de conversión u obligatoriedad lanzado solo, y cambios por código cuando el negocio lo pide (`new HttpErrorMapper({ statusByCode: { ORD001: 409 } })`).
96
+ `error.code` es `'ORD002'`. En la entrada, `ExceptionClassifier.classify(error)` (de `@archi-code/ddd/infrastructure`) da el tipo de fallo sin tablas por excepción: `ExceptionKind.Invalid` para una `ValidationException` o cualquier `DomainException` (422 en HTTP) y `ExceptionKind.Malformed` para un error de conversión u obligatoriedad lanzado solo (400); el punto de entrada cambia el estado de un código cuando el negocio lo pide (`ORD001` → 409).
@@ -39,8 +39,7 @@ El ejemplo canónico de *DDD in PHP* (moneda e importe juntos) [PHP: Value Objec
39
39
 
40
40
  ```ts
41
41
  import { Validations } from '@archi-code/validation';
42
- import { Composite, NumberType, Required, StringType } from '@archi-code/ddd/domain';
43
- import { DomainException } from '@archi-code/ddd/exceptions';
42
+ import { Composite, DomainException, NumberType, Required, StringType } from '@archi-code/ddd/domain';
44
43
 
45
44
  export class CurrencyMismatch extends DomainException {
46
45
  constructor(expected: string, received: string) {
@@ -374,4 +373,4 @@ describe('Money', () => {
374
373
  });
375
374
  ```
376
375
 
377
- `ValidationException` se importa de `@archi-code/ddd/exceptions`.
376
+ `ValidationException` se importa de `@archi-code/ddd/domain`.