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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/LICENSE +46 -0
  2. package/README.md +2795 -2
  3. package/SKILL.md +106 -0
  4. package/ddd-skills/archi-code-ddd/imports.md +94 -0
  5. package/ddd-skills/archi-code-ddd/layers.md +314 -0
  6. package/ddd-skills/archi-code-ddd/patterns.md +683 -0
  7. package/ddd-skills/archi-code-ddd/what-not-to-do.md +196 -0
  8. package/ddd-skills/archi-code-ddd/where-each-class-goes.md +64 -0
  9. package/ddd-skills/ddd-agregados/GUIA.md +114 -0
  10. package/ddd-skills/ddd-agregados/references/consistencia-eventual.md +158 -0
  11. package/ddd-skills/ddd-agregados/references/ejemplo-ecommerce.md +52 -0
  12. package/ddd-skills/ddd-agregados/references/fabricas.md +193 -0
  13. package/ddd-skills/ddd-agregados/references/implementacion-ts.md +706 -0
  14. package/ddd-skills/ddd-arquitectura/GUIA.md +80 -0
  15. package/ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs +99 -0
  16. package/ddd-skills/ddd-arquitectura/references/estilos-adicionales.md +51 -0
  17. package/ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md +406 -0
  18. package/ddd-skills/ddd-arquitectura/references/modulos.md +48 -0
  19. package/ddd-skills/ddd-auditoria-refactorizacion/GUIA.md +85 -0
  20. package/ddd-skills/ddd-auditoria-refactorizacion/references/ejemplo-auditoria.md +314 -0
  21. package/ddd-skills/ddd-auditoria-refactorizacion/references/lista-comprobacion.md +54 -0
  22. package/ddd-skills/ddd-auditoria-refactorizacion/references/plan-migracion.md +73 -0
  23. package/ddd-skills/ddd-auditoria-refactorizacion/scripts/scan_ddd_smells.py +264 -0
  24. package/ddd-skills/ddd-context-mapping/GUIA.md +83 -0
  25. package/ddd-skills/ddd-context-mapping/references/anticorrupcion-ts.md +419 -0
  26. package/ddd-skills/ddd-context-mapping/references/estilos-integracion.md +152 -0
  27. package/ddd-skills/ddd-event-sourcing-cqrs/GUIA.md +92 -0
  28. package/ddd-skills/ddd-event-sourcing-cqrs/references/event-sourcing-ts.md +476 -0
  29. package/ddd-skills/ddd-event-sourcing-cqrs/references/proyecciones-cqrs-ts.md +285 -0
  30. package/ddd-skills/ddd-eventos-dominio/GUIA.md +106 -0
  31. package/ddd-skills/ddd-eventos-dominio/references/diseno-eventos.md +130 -0
  32. package/ddd-skills/ddd-eventos-dominio/references/ejemplo-ecommerce.md +240 -0
  33. package/ddd-skills/ddd-eventos-dominio/references/publicacion-ts.md +529 -0
  34. package/ddd-skills/ddd-orquestador/GUIA.md +40 -0
  35. package/ddd-skills/ddd-orquestador/references/chuleta-reglas.md +35 -0
  36. package/ddd-skills/ddd-repositorios-persistencia/GUIA.md +121 -0
  37. package/ddd-skills/ddd-repositorios-persistencia/references/implementacion-ts.md +314 -0
  38. package/ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md +191 -0
  39. package/ddd-skills/ddd-repositorios-persistencia/references/transacciones-ts.md +301 -0
  40. package/ddd-skills/ddd-servicios-capa-aplicacion/GUIA.md +99 -0
  41. package/ddd-skills/ddd-servicios-capa-aplicacion/references/capa-aplicacion-ts.md +264 -0
  42. package/ddd-skills/ddd-servicios-capa-aplicacion/references/ejemplo-ecommerce.md +321 -0
  43. package/ddd-skills/ddd-servicios-capa-aplicacion/references/servicios-dominio-ts.md +190 -0
  44. package/ddd-skills/ddd-value-objects-entidades/GUIA.md +118 -0
  45. package/ddd-skills/ddd-value-objects-entidades/references/ejemplo-ecommerce.md +87 -0
  46. package/ddd-skills/ddd-value-objects-entidades/references/entidades-ts.md +218 -0
  47. package/ddd-skills/ddd-value-objects-entidades/references/persistencia-valores.md +88 -0
  48. package/ddd-skills/ddd-value-objects-entidades/references/specification-y-diseno-flexible.md +119 -0
  49. package/ddd-skills/ddd-value-objects-entidades/references/validacion.md +96 -0
  50. package/ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md +376 -0
  51. package/dist/cjs/application/event/event-bus.d.ts +4 -0
  52. package/dist/cjs/application/event/event-bus.js +2 -0
  53. package/dist/cjs/application/event/event-listener.d.ts +4 -0
  54. package/dist/cjs/application/event/event-listener.js +6 -0
  55. package/dist/cjs/application/event/listens-to.d.ts +15 -0
  56. package/dist/cjs/application/event/listens-to.js +28 -0
  57. package/dist/cjs/application/exceptions/aggregate-not-found.exception.d.ts +5 -0
  58. package/dist/cjs/application/exceptions/aggregate-not-found.exception.js +17 -0
  59. package/dist/cjs/application/exceptions/application-exception-code.d.ts +7 -0
  60. package/dist/cjs/application/exceptions/application-exception-code.js +11 -0
  61. package/dist/cjs/application/exceptions/application.exception.d.ts +6 -0
  62. package/dist/cjs/application/exceptions/application.exception.js +16 -0
  63. package/dist/cjs/application/exceptions/concurrency.exception.d.ts +5 -0
  64. package/dist/cjs/application/exceptions/concurrency.exception.js +17 -0
  65. package/dist/cjs/application/exceptions/not-allowed.exception.d.ts +5 -0
  66. package/dist/cjs/application/exceptions/not-allowed.exception.js +17 -0
  67. package/dist/cjs/application/exceptions/unexpected-error.exception.d.ts +5 -0
  68. package/dist/cjs/application/exceptions/unexpected-error.exception.js +17 -0
  69. package/dist/cjs/application/index.d.ts +14 -0
  70. package/dist/cjs/application/index.js +23 -0
  71. package/dist/cjs/application/ports/clock.d.ts +3 -0
  72. package/dist/cjs/application/ports/clock.js +2 -0
  73. package/dist/cjs/application/ports/logger.d.ts +8 -0
  74. package/dist/cjs/application/ports/logger.js +2 -0
  75. package/dist/cjs/application/use-case/use-case.d.ts +6 -0
  76. package/dist/cjs/application/use-case/use-case.js +25 -0
  77. package/dist/cjs/application/value-object-builder/value-object-builder.d.ts +16 -0
  78. package/dist/cjs/application/value-object-builder/value-object-builder.js +51 -0
  79. package/dist/cjs/domain/aggregate/aggregate-root.d.ts +9 -0
  80. package/dist/cjs/domain/aggregate/aggregate-root.js +17 -0
  81. package/dist/cjs/domain/entity/entity.d.ts +9 -0
  82. package/dist/cjs/domain/entity/entity.js +19 -0
  83. package/dist/cjs/domain/event/event-base.d.ts +23 -0
  84. package/dist/cjs/domain/event/event-base.js +21 -0
  85. package/dist/cjs/domain/exceptions/domain-exception-code.d.ts +7 -0
  86. package/dist/cjs/domain/exceptions/domain-exception-code.js +11 -0
  87. package/dist/cjs/domain/exceptions/domain.exception.d.ts +6 -0
  88. package/dist/cjs/domain/exceptions/domain.exception.js +16 -0
  89. package/dist/cjs/domain/exceptions/invalid-event-definition.exception.d.ts +4 -0
  90. package/dist/cjs/domain/exceptions/invalid-event-definition.exception.js +19 -0
  91. package/dist/cjs/domain/exceptions/required-value.exception.d.ts +4 -0
  92. package/dist/cjs/domain/exceptions/required-value.exception.js +20 -0
  93. package/dist/cjs/domain/exceptions/type-primitive.exception.d.ts +4 -0
  94. package/dist/cjs/domain/exceptions/type-primitive.exception.js +20 -0
  95. package/dist/cjs/domain/exceptions/validation.exception.d.ts +14 -0
  96. package/dist/cjs/domain/exceptions/validation.exception.js +23 -0
  97. package/dist/cjs/domain/index.d.ts +17 -0
  98. package/dist/cjs/domain/index.js +36 -0
  99. package/dist/cjs/domain/primitive/primitive-types.d.ts +20 -0
  100. package/dist/cjs/domain/primitive/primitive-types.js +2 -0
  101. package/dist/cjs/domain/repository/aggregate-finder.d.ts +4 -0
  102. package/dist/cjs/domain/repository/aggregate-finder.js +2 -0
  103. package/dist/cjs/domain/repository/aggregate-remover.d.ts +4 -0
  104. package/dist/cjs/domain/repository/aggregate-remover.js +2 -0
  105. package/dist/cjs/domain/repository/aggregate-saver.d.ts +4 -0
  106. package/dist/cjs/domain/repository/aggregate-saver.js +2 -0
  107. package/dist/cjs/domain/specification/specification.d.ts +9 -0
  108. package/dist/cjs/domain/specification/specification.js +44 -0
  109. package/dist/cjs/domain/type/abstract-type.d.ts +18 -0
  110. package/dist/cjs/domain/type/abstract-type.js +61 -0
  111. package/dist/cjs/domain/type/array-type.d.ts +17 -0
  112. package/dist/cjs/domain/type/array-type.js +64 -0
  113. package/dist/cjs/domain/type/boolean-type.d.ts +4 -0
  114. package/dist/cjs/domain/type/boolean-type.js +16 -0
  115. package/dist/cjs/domain/type/composite/composite-brand.d.ts +4 -0
  116. package/dist/cjs/domain/type/composite/composite-brand.js +15 -0
  117. package/dist/cjs/domain/type/composite/composite-field.d.ts +18 -0
  118. package/dist/cjs/domain/type/composite/composite-field.js +86 -0
  119. package/dist/cjs/domain/type/composite/composite-issues.d.ts +6 -0
  120. package/dist/cjs/domain/type/composite/composite-issues.js +35 -0
  121. package/dist/cjs/domain/type/composite/composite.d.ts +26 -0
  122. package/dist/cjs/domain/type/composite/composite.js +51 -0
  123. package/dist/cjs/domain/type/date-type.d.ts +19 -0
  124. package/dist/cjs/domain/type/date-type.js +70 -0
  125. package/dist/cjs/domain/type/enum-type.d.ts +7 -0
  126. package/dist/cjs/domain/type/enum-type.js +30 -0
  127. package/dist/cjs/domain/type/id-type.d.ts +7 -0
  128. package/dist/cjs/domain/type/id-type.js +17 -0
  129. package/dist/cjs/domain/type/index.d.ts +15 -0
  130. package/dist/cjs/domain/type/index.js +29 -0
  131. package/dist/cjs/domain/type/json-type.d.ts +9 -0
  132. package/dist/cjs/domain/type/json-type.js +46 -0
  133. package/dist/cjs/domain/type/number-type.d.ts +4 -0
  134. package/dist/cjs/domain/type/number-type.js +16 -0
  135. package/dist/cjs/domain/type/required/required-mark.d.ts +8 -0
  136. package/dist/cjs/domain/type/required/required-mark.js +13 -0
  137. package/dist/cjs/domain/type/required/required-type.d.ts +7 -0
  138. package/dist/cjs/domain/type/required/required-type.js +21 -0
  139. package/dist/cjs/domain/type/string-type.d.ts +4 -0
  140. package/dist/cjs/domain/type/string-type.js +16 -0
  141. package/dist/cjs/domain/type/uuid-type.d.ts +7 -0
  142. package/dist/cjs/domain/type/uuid-type.js +27 -0
  143. package/dist/cjs/infrastructure/clock/fixed-clock.d.ts +8 -0
  144. package/dist/cjs/infrastructure/clock/fixed-clock.js +19 -0
  145. package/dist/cjs/infrastructure/clock/system-clock.d.ts +4 -0
  146. package/dist/cjs/infrastructure/clock/system-clock.js +9 -0
  147. package/dist/cjs/infrastructure/event/domain-event-bus.d.ts +16 -0
  148. package/dist/cjs/infrastructure/event/domain-event-bus.js +60 -0
  149. package/dist/cjs/infrastructure/event/event-channel.d.ts +9 -0
  150. package/dist/cjs/infrastructure/event/event-channel.js +2 -0
  151. package/dist/cjs/infrastructure/event/event-registry.d.ts +9 -0
  152. package/dist/cjs/infrastructure/event/event-registry.js +43 -0
  153. package/dist/cjs/infrastructure/event/in-memory/emitter.d.ts +11 -0
  154. package/dist/cjs/infrastructure/event/in-memory/emitter.js +54 -0
  155. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.d.ts +16 -0
  156. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.js +50 -0
  157. package/dist/cjs/infrastructure/exceptions/event-delivery.exception.d.ts +5 -0
  158. package/dist/cjs/infrastructure/exceptions/event-delivery.exception.js +20 -0
  159. package/dist/cjs/infrastructure/exceptions/infrastructure-exception-code.d.ts +6 -0
  160. package/dist/cjs/infrastructure/exceptions/infrastructure-exception-code.js +10 -0
  161. package/dist/cjs/infrastructure/exceptions/infrastructure.exception.d.ts +6 -0
  162. package/dist/cjs/infrastructure/exceptions/infrastructure.exception.js +16 -0
  163. package/dist/cjs/infrastructure/exceptions/internal-error.exception.d.ts +5 -0
  164. package/dist/cjs/infrastructure/exceptions/internal-error.exception.js +17 -0
  165. package/dist/cjs/infrastructure/exceptions/kind/exception-classifier.d.ts +4 -0
  166. package/dist/cjs/infrastructure/exceptions/kind/exception-classifier.js +34 -0
  167. package/dist/cjs/infrastructure/exceptions/kind/exception-kind.d.ts +9 -0
  168. package/dist/cjs/infrastructure/exceptions/kind/exception-kind.js +13 -0
  169. package/dist/cjs/infrastructure/exceptions/unknown-event.exception.d.ts +4 -0
  170. package/dist/cjs/infrastructure/exceptions/unknown-event.exception.js +19 -0
  171. package/dist/cjs/infrastructure/index.d.ts +21 -0
  172. package/dist/cjs/infrastructure/index.js +33 -0
  173. package/dist/cjs/infrastructure/logging/console-logger.d.ts +17 -0
  174. package/dist/cjs/infrastructure/logging/console-logger.js +37 -0
  175. package/dist/cjs/infrastructure/logging/in-memory-logger.d.ts +15 -0
  176. package/dist/cjs/infrastructure/logging/in-memory-logger.js +27 -0
  177. package/dist/cjs/infrastructure/logging/log-serializer.d.ts +7 -0
  178. package/dist/cjs/infrastructure/logging/log-serializer.js +59 -0
  179. package/dist/cjs/infrastructure/logging/silent-logger.d.ts +7 -0
  180. package/dist/cjs/infrastructure/logging/silent-logger.js +18 -0
  181. package/dist/cjs/shared/exceptions/abstract.exception.d.ts +25 -0
  182. package/dist/cjs/shared/exceptions/abstract.exception.js +59 -0
  183. package/dist/cjs/shared/exceptions/brand/exception-brand.d.ts +6 -0
  184. package/dist/cjs/shared/exceptions/brand/exception-brand.js +29 -0
  185. package/dist/cjs/shared/exceptions/received/received-value.d.ts +4 -0
  186. package/dist/cjs/shared/exceptions/received/received-value.js +13 -0
  187. package/dist/cjs/shared/index.d.ts +8 -0
  188. package/dist/cjs/shared/index.js +13 -0
  189. package/dist/cjs/shared/issue-collection-error.d.ts +9 -0
  190. package/dist/cjs/shared/issue-collection-error.js +11 -0
  191. package/dist/cjs/shared/issue-collector.d.ts +8 -0
  192. package/dist/cjs/shared/issue-collector.js +41 -0
  193. package/dist/esm/application/event/event-bus.d.ts +4 -0
  194. package/dist/esm/application/event/event-bus.js +1 -0
  195. package/dist/esm/application/event/event-listener.d.ts +4 -0
  196. package/dist/esm/application/event/event-listener.js +2 -0
  197. package/dist/esm/application/event/listens-to.d.ts +15 -0
  198. package/dist/esm/application/event/listens-to.js +25 -0
  199. package/dist/esm/application/exceptions/aggregate-not-found.exception.d.ts +5 -0
  200. package/dist/esm/application/exceptions/aggregate-not-found.exception.js +13 -0
  201. package/dist/esm/application/exceptions/application-exception-code.d.ts +7 -0
  202. package/dist/esm/application/exceptions/application-exception-code.js +8 -0
  203. package/dist/esm/application/exceptions/application.exception.d.ts +6 -0
  204. package/dist/esm/application/exceptions/application.exception.js +12 -0
  205. package/dist/esm/application/exceptions/concurrency.exception.d.ts +5 -0
  206. package/dist/esm/application/exceptions/concurrency.exception.js +13 -0
  207. package/dist/esm/application/exceptions/not-allowed.exception.d.ts +5 -0
  208. package/dist/esm/application/exceptions/not-allowed.exception.js +13 -0
  209. package/dist/esm/application/exceptions/unexpected-error.exception.d.ts +5 -0
  210. package/dist/esm/application/exceptions/unexpected-error.exception.js +13 -0
  211. package/dist/esm/application/index.d.ts +14 -0
  212. package/dist/esm/application/index.js +10 -0
  213. package/dist/esm/application/ports/clock.d.ts +3 -0
  214. package/dist/esm/application/ports/clock.js +1 -0
  215. package/dist/esm/application/ports/logger.d.ts +8 -0
  216. package/dist/esm/application/ports/logger.js +1 -0
  217. package/dist/esm/application/use-case/use-case.d.ts +6 -0
  218. package/dist/esm/application/use-case/use-case.js +21 -0
  219. package/dist/esm/application/value-object-builder/value-object-builder.d.ts +16 -0
  220. package/dist/esm/application/value-object-builder/value-object-builder.js +47 -0
  221. package/dist/esm/domain/aggregate/aggregate-root.d.ts +9 -0
  222. package/dist/esm/domain/aggregate/aggregate-root.js +13 -0
  223. package/dist/esm/domain/entity/entity.d.ts +9 -0
  224. package/dist/esm/domain/entity/entity.js +15 -0
  225. package/dist/esm/domain/event/event-base.d.ts +23 -0
  226. package/dist/esm/domain/event/event-base.js +17 -0
  227. package/dist/esm/domain/exceptions/domain-exception-code.d.ts +7 -0
  228. package/dist/esm/domain/exceptions/domain-exception-code.js +8 -0
  229. package/dist/esm/domain/exceptions/domain.exception.d.ts +6 -0
  230. package/dist/esm/domain/exceptions/domain.exception.js +12 -0
  231. package/dist/esm/domain/exceptions/invalid-event-definition.exception.d.ts +4 -0
  232. package/dist/esm/domain/exceptions/invalid-event-definition.exception.js +15 -0
  233. package/dist/esm/domain/exceptions/required-value.exception.d.ts +4 -0
  234. package/dist/esm/domain/exceptions/required-value.exception.js +16 -0
  235. package/dist/esm/domain/exceptions/type-primitive.exception.d.ts +4 -0
  236. package/dist/esm/domain/exceptions/type-primitive.exception.js +16 -0
  237. package/dist/esm/domain/exceptions/validation.exception.d.ts +14 -0
  238. package/dist/esm/domain/exceptions/validation.exception.js +19 -0
  239. package/dist/esm/domain/index.d.ts +17 -0
  240. package/dist/esm/domain/index.js +12 -0
  241. package/dist/esm/domain/primitive/primitive-types.d.ts +20 -0
  242. package/dist/esm/domain/primitive/primitive-types.js +1 -0
  243. package/dist/esm/domain/repository/aggregate-finder.d.ts +4 -0
  244. package/dist/esm/domain/repository/aggregate-finder.js +1 -0
  245. package/dist/esm/domain/repository/aggregate-remover.d.ts +4 -0
  246. package/dist/esm/domain/repository/aggregate-remover.js +1 -0
  247. package/dist/esm/domain/repository/aggregate-saver.d.ts +4 -0
  248. package/dist/esm/domain/repository/aggregate-saver.js +1 -0
  249. package/dist/esm/domain/specification/specification.d.ts +9 -0
  250. package/dist/esm/domain/specification/specification.js +40 -0
  251. package/dist/esm/domain/type/abstract-type.d.ts +18 -0
  252. package/dist/esm/domain/type/abstract-type.js +57 -0
  253. package/dist/esm/domain/type/array-type.d.ts +17 -0
  254. package/dist/esm/domain/type/array-type.js +60 -0
  255. package/dist/esm/domain/type/boolean-type.d.ts +4 -0
  256. package/dist/esm/domain/type/boolean-type.js +12 -0
  257. package/dist/esm/domain/type/composite/composite-brand.d.ts +4 -0
  258. package/dist/esm/domain/type/composite/composite-brand.js +11 -0
  259. package/dist/esm/domain/type/composite/composite-field.d.ts +18 -0
  260. package/dist/esm/domain/type/composite/composite-field.js +82 -0
  261. package/dist/esm/domain/type/composite/composite-issues.d.ts +6 -0
  262. package/dist/esm/domain/type/composite/composite-issues.js +31 -0
  263. package/dist/esm/domain/type/composite/composite.d.ts +26 -0
  264. package/dist/esm/domain/type/composite/composite.js +48 -0
  265. package/dist/esm/domain/type/date-type.d.ts +19 -0
  266. package/dist/esm/domain/type/date-type.js +64 -0
  267. package/dist/esm/domain/type/enum-type.d.ts +7 -0
  268. package/dist/esm/domain/type/enum-type.js +26 -0
  269. package/dist/esm/domain/type/id-type.d.ts +7 -0
  270. package/dist/esm/domain/type/id-type.js +13 -0
  271. package/dist/esm/domain/type/index.d.ts +15 -0
  272. package/dist/esm/domain/type/index.js +12 -0
  273. package/dist/esm/domain/type/json-type.d.ts +9 -0
  274. package/dist/esm/domain/type/json-type.js +42 -0
  275. package/dist/esm/domain/type/number-type.d.ts +4 -0
  276. package/dist/esm/domain/type/number-type.js +12 -0
  277. package/dist/esm/domain/type/required/required-mark.d.ts +8 -0
  278. package/dist/esm/domain/type/required/required-mark.js +9 -0
  279. package/dist/esm/domain/type/required/required-type.d.ts +7 -0
  280. package/dist/esm/domain/type/required/required-type.js +18 -0
  281. package/dist/esm/domain/type/string-type.d.ts +4 -0
  282. package/dist/esm/domain/type/string-type.js +12 -0
  283. package/dist/esm/domain/type/uuid-type.d.ts +7 -0
  284. package/dist/esm/domain/type/uuid-type.js +23 -0
  285. package/dist/esm/infrastructure/clock/fixed-clock.d.ts +8 -0
  286. package/dist/esm/infrastructure/clock/fixed-clock.js +15 -0
  287. package/dist/esm/infrastructure/clock/system-clock.d.ts +4 -0
  288. package/dist/esm/infrastructure/clock/system-clock.js +5 -0
  289. package/dist/esm/infrastructure/event/domain-event-bus.d.ts +16 -0
  290. package/dist/esm/infrastructure/event/domain-event-bus.js +56 -0
  291. package/dist/esm/infrastructure/event/event-channel.d.ts +9 -0
  292. package/dist/esm/infrastructure/event/event-channel.js +1 -0
  293. package/dist/esm/infrastructure/event/event-registry.d.ts +9 -0
  294. package/dist/esm/infrastructure/event/event-registry.js +39 -0
  295. package/dist/esm/infrastructure/event/in-memory/emitter.d.ts +11 -0
  296. package/dist/esm/infrastructure/event/in-memory/emitter.js +50 -0
  297. package/dist/esm/infrastructure/event/in-memory/in-memory-event-channel.d.ts +16 -0
  298. package/dist/esm/infrastructure/event/in-memory/in-memory-event-channel.js +46 -0
  299. package/dist/esm/infrastructure/exceptions/event-delivery.exception.d.ts +5 -0
  300. package/dist/esm/infrastructure/exceptions/event-delivery.exception.js +16 -0
  301. package/dist/esm/infrastructure/exceptions/infrastructure-exception-code.d.ts +6 -0
  302. package/dist/esm/infrastructure/exceptions/infrastructure-exception-code.js +7 -0
  303. package/dist/esm/infrastructure/exceptions/infrastructure.exception.d.ts +6 -0
  304. package/dist/esm/infrastructure/exceptions/infrastructure.exception.js +12 -0
  305. package/dist/esm/infrastructure/exceptions/internal-error.exception.d.ts +5 -0
  306. package/dist/esm/infrastructure/exceptions/internal-error.exception.js +13 -0
  307. package/dist/esm/infrastructure/exceptions/kind/exception-classifier.d.ts +4 -0
  308. package/dist/esm/infrastructure/exceptions/kind/exception-classifier.js +30 -0
  309. package/dist/esm/infrastructure/exceptions/kind/exception-kind.d.ts +9 -0
  310. package/dist/esm/infrastructure/exceptions/kind/exception-kind.js +10 -0
  311. package/dist/esm/infrastructure/exceptions/unknown-event.exception.d.ts +4 -0
  312. package/dist/esm/infrastructure/exceptions/unknown-event.exception.js +15 -0
  313. package/dist/esm/infrastructure/index.d.ts +21 -0
  314. package/dist/esm/infrastructure/index.js +15 -0
  315. package/dist/esm/infrastructure/logging/console-logger.d.ts +17 -0
  316. package/dist/esm/infrastructure/logging/console-logger.js +33 -0
  317. package/dist/esm/infrastructure/logging/in-memory-logger.d.ts +15 -0
  318. package/dist/esm/infrastructure/logging/in-memory-logger.js +23 -0
  319. package/dist/esm/infrastructure/logging/log-serializer.d.ts +7 -0
  320. package/dist/esm/infrastructure/logging/log-serializer.js +55 -0
  321. package/dist/esm/infrastructure/logging/silent-logger.d.ts +7 -0
  322. package/dist/esm/infrastructure/logging/silent-logger.js +14 -0
  323. package/dist/esm/package.json +3 -0
  324. package/dist/esm/shared/exceptions/abstract.exception.d.ts +25 -0
  325. package/dist/esm/shared/exceptions/abstract.exception.js +55 -0
  326. package/dist/esm/shared/exceptions/brand/exception-brand.d.ts +6 -0
  327. package/dist/esm/shared/exceptions/brand/exception-brand.js +25 -0
  328. package/dist/esm/shared/exceptions/received/received-value.d.ts +4 -0
  329. package/dist/esm/shared/exceptions/received/received-value.js +9 -0
  330. package/dist/esm/shared/index.d.ts +8 -0
  331. package/dist/esm/shared/index.js +5 -0
  332. package/dist/esm/shared/issue-collection-error.d.ts +9 -0
  333. package/dist/esm/shared/issue-collection-error.js +7 -0
  334. package/dist/esm/shared/issue-collector.d.ts +8 -0
  335. package/dist/esm/shared/issue-collector.js +37 -0
  336. package/package.json +95 -4
@@ -0,0 +1,190 @@
1
+ # Servicios de dominio en TypeScript
2
+
3
+ ## Cuándo
4
+
5
+ IDDD enumera tres situaciones [IDDD cap. 7]:
6
+ * realizar un proceso de negocio significativo;
7
+ * transformar un objeto de dominio de una composición a otra;
8
+ * calcular un valor que requiere datos de más de un objeto de dominio (el caso más común).
9
+
10
+ Ejemplo de IDDD: autenticar a un usuario solo si su tenant está activo. Ponerlo en `User` obliga al cliente a conocer `Tenant` y las reglas de cifrado; un servicio `AuthenticationService` expresa la operación del lenguaje y oculta los detalles [IDDD cap. 7].
11
+
12
+ ## Forma
13
+
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
+
16
+ ```ts
17
+ import { Composite, DomainException, Required, StringType } from '@archi-code/ddd/domain';
18
+ import type { SatisfiedBy } from '@archi-code/ddd/domain';
19
+ import { Validations } from '@archi-code/validation';
20
+
21
+ @Validations([{ validator: 'isIn', properties: { values: ['PENINSULA', 'BALEARES', 'CANARIAS'] } }])
22
+ export class ShippingZone extends Required(StringType) {}
23
+
24
+ export class ShippingRate extends Composite({ zone: ShippingZone, price: Money }) {}
25
+
26
+ export class NoShippingToZone extends DomainException {
27
+ constructor(zone: string) {
28
+ super(`There is no shipping rate for ${zone}`, ['SHP001']);
29
+ }
30
+ }
31
+
32
+ export class ShippingRates extends Composite({ items: [ShippingRate] }) {
33
+ priceFor(zone: ShippingZone): Money {
34
+ const rate = this.items.find((candidate) => candidate.zone.equals(zone));
35
+ if (rate === undefined) {
36
+ throw new NoShippingToZone(zone.value);
37
+ }
38
+ return rate.price;
39
+ }
40
+ }
41
+
42
+ export class ShippingCostCalculator {
43
+ costFor(
44
+ order: Order,
45
+ zone: ShippingZone,
46
+ rates: ShippingRates,
47
+ freeShipping: SatisfiedBy<Order>,
48
+ ): Money {
49
+ const price = rates.priceFor(zone);
50
+ if (!freeShipping.isSatisfiedBy(order)) {
51
+ return price;
52
+ }
53
+ return new Money({ amount: new Cents(0), currency: price.currency });
54
+ }
55
+ }
56
+ ```
57
+
58
+ * Sin estado; recibe objetos de dominio (`Order`, la tabla de tarifas como composite, la zona) y devuelve un value object (`Money`, el composite de `Cents` y `Currency`).
59
+ * La condición de envío gratis es una `Specification<Order>` que recibe ya construida (`new OrderAbove(euros(5000))`); `SatisfiedBy<Order>` acepta también cualquier objeto con `isSatisfiedBy`, y se combina con `and`, `or` y `not`.
60
+ * El nombre de la clase y del método salen del lenguaje ubicuo.
61
+ * No guarda nada ni publica nada; eso lo hace el caso de uso que lo invoca.
62
+ * El servicio puede leer por repositorio [IDDD cap. 7]: con el paquete recibe un `AggregateFinder` (solo lectura) o los agregados ya cargados por el caso de uso, como aquí; nunca guarda ni elimina.
63
+
64
+ ## Interfaz separada o no
65
+
66
+ Si solo habrá una implementación y no depende de infraestructura, una clase concreta basta. IDDD discute la interfaz separada y advierte contra nombres de implementación sin significado como `Impl`: si hay interfaz, la implementación se nombra por lo que la distingue [IDDD cap. 7].
67
+
68
+ Cuando la implementación necesita tecnología (consultar otro contexto, un servicio de tipos de cambio), la interfaz va en el dominio, con value objects, y la implementación en infraestructura [IDDD cap. 7]. El adaptador envuelve los errores del cliente en `InternalErrorException` con `cause` y redondea de forma explícita (el dinero va en céntimos enteros):
69
+
70
+ ```ts
71
+ import { DateType, Required } from '@archi-code/ddd/domain';
72
+
73
+ export class RateDay extends Required(DateType) {}
74
+
75
+ export interface ExchangeRates {
76
+ convert(amount: Money, to: Currency, on: RateDay): Promise<Money>;
77
+ }
78
+ ```
79
+
80
+ ```ts
81
+ import { InternalErrorException } from '@archi-code/ddd/infrastructure';
82
+
83
+ interface EcbClient {
84
+ rate(from: string, to: string, day: string): Promise<number>;
85
+ }
86
+
87
+ export class EcbExchangeRates implements ExchangeRates {
88
+ constructor(private readonly ecb: EcbClient) {}
89
+
90
+ async convert(amount: Money, to: Currency, on: RateDay): Promise<Money> {
91
+ let rate: number;
92
+ try {
93
+ rate = await this.ecb.rate(amount.currency.value, to.value, on.value.toISOString().slice(0, 10));
94
+ } catch (error) {
95
+ throw new InternalErrorException('reading the ECB exchange rates', { cause: error });
96
+ }
97
+ return new Money({ amount: new Cents(Math.round(amount.amount.value * rate)), currency: to });
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Validación de composiciones
103
+
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.
105
+
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:
107
+
108
+ ```ts
109
+ import { ValidationException } from '@archi-code/ddd/domain';
110
+ import type { ValidationIssue } from '@archi-code/ddd/domain';
111
+
112
+ export class ShipmentCheck {
113
+ assertCanShip(order: Order, customer: Customer): void {
114
+ const issues: ValidationIssue[] = [];
115
+ if (!order.customerId.equals(customer.id)) {
116
+ issues.push({ field: 'customer', code: 'SHP002', message: `Order ${order.id.value} belongs to another customer` });
117
+ }
118
+ if (customer.blocked.value) {
119
+ issues.push({ field: 'customer', code: 'CUS001', message: `Customer ${customer.id.value} is blocked` });
120
+ }
121
+ if (!order.status.isPending()) {
122
+ issues.push({ field: 'order', code: 'ORD001', message: `Order ${order.id.value} is no longer pending` });
123
+ }
124
+ if (issues.length > 0) {
125
+ throw new ValidationException(issues);
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ ## Tests
132
+
133
+ Los servicios se prueban como cualquier objeto de dominio, con datos realistas y nombres del lenguaje [IDDD cap. 7]. Con Vitest, las fábricas de datos de prueba (`anOrderWorth`, `euros`, `standardRates`) viven en `test/support/` del proyecto:
134
+
135
+ ```ts
136
+ import { describe, expect, it } from 'vitest';
137
+
138
+ describe('ShippingCostCalculator', () => {
139
+ it('ships for free to the peninsula from 50 EUR', () => {
140
+ const cost = new ShippingCostCalculator().costFor(
141
+ anOrderWorth(5200),
142
+ new ShippingZone('PENINSULA'),
143
+ standardRates(),
144
+ new OrderAbove(euros(5000)),
145
+ );
146
+
147
+ expect(cost.toPrimitives()).toEqual({ amount: 0, currency: 'EUR' });
148
+ });
149
+
150
+ it('charges the rate of the Canary Islands below the minimum', () => {
151
+ const cost = new ShippingCostCalculator().costFor(
152
+ anOrderWorth(3000),
153
+ new ShippingZone('CANARIAS'),
154
+ standardRates(),
155
+ new OrderAbove(euros(5000)),
156
+ );
157
+
158
+ expect(cost.equals(euros(1295))).toBe(true);
159
+ });
160
+ });
161
+ ```
162
+
163
+ ```ts
164
+ import { FixedClock } from '@archi-code/ddd/infrastructure';
165
+
166
+ const clock = new FixedClock('2026-03-01T09:30:00Z');
167
+
168
+ export const euros = (cents: number): Money => Money.fromPrimitives({ amount: cents, currency: 'EUR' });
169
+
170
+ export const anOrderWorth = (cents: number): Order =>
171
+ Order.place(
172
+ OrderId.generate(),
173
+ new CustomerId('3c1d2e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f'),
174
+ OrderLines.fromPrimitives({
175
+ items: [{ sku: 'TORCH-01', quantity: 1, unitPrice: { amount: cents, currency: 'EUR' } }],
176
+ }),
177
+ ShippingAddress.fromPrimitives({ street: 'Calle Mayor 1', city: 'Huelva', country: 'ES' }),
178
+ clock,
179
+ );
180
+
181
+ export const standardRates = (): ShippingRates =>
182
+ ShippingRates.fromPrimitives({
183
+ items: [
184
+ { zone: 'PENINSULA', price: { amount: 495, currency: 'EUR' } },
185
+ { zone: 'CANARIAS', price: { amount: 1295, currency: 'EUR' } },
186
+ ],
187
+ });
188
+ ```
189
+
190
+ `Order`, `OrderLines`, `ShippingAddress`, `Money` y `Cents` son los de `ddd-skills/ddd-agregados/references/implementacion-ts.md`, y `OrderAbove` y `Customer`, los de `ddd-skills/ddd-servicios-capa-aplicacion/references/ejemplo-ecommerce.md`.
@@ -0,0 +1,118 @@
1
+ # Value Objects y entidades
2
+
3
+ > Parte del skill `archi-code-ddd`: léela solo cuando ese skill te dirija aquí. Los ejemplos están escritos con la API de `@archi-code/ddd` (las clases de su `src`) y se copian tal cual; las guías son la fuente de verdad: el paquete solo se aparta de ellas donde lo marca `[Fuera de los libros: convención de @archi-code/ddd]`, y cualquier otra discrepancia es un defecto del paquete.
4
+ >
5
+ > **Cuándo consultar esta guía.** Diseño táctico DDD de Value Objects y entidades en TypeScript. Decide si un concepto es value object o entidad, cómo implementar value objects inmutables con igualdad por valor y comportamiento sin efectos secundarios (Money, identificadores, rangos, direcciones, tipos estándar y estados), cómo diseñar entidades con identidad estable y comportamiento con nombres del negocio en lugar de setters, estrategias y momento de generación de identidad, los tres niveles de validación (atributo, objeto completo, composición) y cómo persistir value objects sin que la base de datos deforme el modelo. Consúltala siempre que se cree o revise una clase del dominio, aparezcan primitivos sueltos (string para emails, number para dinero), getters y setters públicos, dudas sobre igualdad, ids o validaciones, o se pregunte "¿esto es una entidad?", aunque no se mencione DDD.
6
+
7
+ ## Con @archi-code/ddd
8
+
9
+ [Fuera de los libros: convención de @archi-code/ddd]
10
+
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
+ * 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
+ * `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 `ValueObjectBuilder.build({...})` y `ValidationException.errors`, no con un manejador de notificación.
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
+ * 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
+ * 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.
18
+
19
+ Dos piezas básicas del modelo. Vernon insiste en un sesgo concreto: **modela con value objects siempre que puedas**. Incluso una entidad debería ser sobre todo un contenedor de value objects, no de entidades hijas, porque los valores son más fáciles de crear, probar, usar, optimizar y mantener [IDDD cap. 6].
20
+
21
+
22
+ ## Paso 1. ¿Value object o entidad?
23
+
24
+ Aplica estas preguntas en orden [IDDD cap. 5 y cap. 6]:
25
+
26
+ 1. ¿Importa distinguir **este** ejemplar de cualquier otro, aunque todos sus atributos coincidan, y seguirlo a lo largo de cambios? Sí → **entidad**.
27
+ 2. Si no, comprueba las características de valor. Si cumple la mayoría → **value object**:
28
+ * mide, cuantifica o describe algo del dominio (no es una cosa en sí);
29
+ * puede ser inmutable;
30
+ * forma un todo conceptual con atributos que solo tienen sentido juntos;
31
+ * se reemplaza entero cuando cambia la descripción;
32
+ * se compara por igualdad de valor;
33
+ * ofrece comportamiento sin efectos secundarios.
34
+ 3. ¿Te inclinas por entidad solo porque la base de datos lo guarda en su propia tabla? Entonces sigue siendo un value object. El modelo de datos se subordina al de dominio, no al revés [IDDD cap. 6].
35
+
36
+ Si dudas, empieza por value object: es más barato pasar de valor a entidad que deshacer una red de entidades [IDDD cap. 6].
37
+
38
+ | Concepto | Decisión típica | Por qué |
39
+ |---|---|---|
40
+ | Importe con moneda | Value object `Money` | Cuantifica; dos "17,00 €" son intercambiables |
41
+ | Dirección de envío de un pedido | Value object | Describe el pedido; si cambia, se reemplaza entera |
42
+ | Dirección en una agenda de clientes con historial y verificación | Puede ser entidad | Se sigue su ciclo de vida |
43
+ | Línea de pedido | Normalmente value object | Se identifica por su producto dentro del pedido |
44
+ | Pedido, Cliente, Pago | Entidad (y raíz de agregado) | Ciclo de vida e identidad propios |
45
+ | `OrderId`, `CustomerId` | Value object que contiene la identidad | Inmutable y estable [IDDD cap. 5] |
46
+ | Estado del pedido | Tipo estándar como value object | Describe el tipo de estado y puede llevar comportamiento [IDDD cap. 6] |
47
+
48
+ ## Paso 2. Implementar
49
+
50
+ * Value objects: `ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md`. Qué clase del paquete usar en cada caso y ejemplos de `Money`, identificadores, `Email`, `DateRange`, colecciones de valores y estados.
51
+ * Entidades: `ddd-skills/ddd-value-objects-entidades/references/entidades-ts.md`. Identidad, `Entity` y `AggregateRoot`, constructor completo, comportamiento con nombres del lenguaje, protección de invariantes, reconstitución con `fromPrimitives`.
52
+
53
+ Reglas mínimas que no se negocian (los nombres de los métodos son interfaces que revelan intención, ver Paso 5):
54
+
55
+ **Value objects**
56
+ * Inmutables: propiedades `readonly`, sin métodos que muten. Las operaciones devuelven nuevas instancias [IDDD cap. 6].
57
+ * Válidos desde su creación: el constructor rechaza estados imposibles (`Required(...)` y `@Validations`) [IDDD cap. 5; PHP: Value Objects].
58
+ * Igualdad por valor con `equals`, que el paquete ya da (clase y valor; fechas por instante, listas por contenido); en TypeScript `===` compara referencias `[Fuera de los libros]`.
59
+ * Los métodos reciben otros value objects, no entidades, para garantizar que no hay efectos secundarios [IDDD cap. 6].
60
+ * Nada de primitivos sueltos para conceptos del dominio: un `string` no puede tener comportamiento del negocio [IDDD cap. 6; PHP: Entities, "Objects vs. Primitive Types"].
61
+
62
+ **Entidades**
63
+ * Identidad estable, protegida contra cambios: se pasa a `super(id)` y queda `readonly` [IDDD cap. 5].
64
+ * Igualdad por identidad: `equals` de `Entity` y `AggregateRoot` compara clase e id.
65
+ * Sin setters públicos: métodos con verbos del negocio que protegen reglas: `backlogItem.commitTo(sprint)` y no `setSprintId(...)` + `setStatus(Committed)`, que deja la regla en cada cliente y el objeto a medias [IDDD cap. 1 y cap. 5].
66
+ * Diseño inicial mínimo: identidad, lo que sirve para encontrarla y el comportamiento esencial con los atributos que ese comportamiento necesita. El resto, después [IDDD cap. 5].
67
+
68
+ ## Paso 3. Identidad
69
+
70
+ Elige la estrategia con `ddd-skills/ddd-value-objects-entidades/references/entidades-ts.md` (sección Identidad). Resumen [IDDD cap. 5; PHP: Entities]:
71
+
72
+ | Estrategia | Riesgo principal |
73
+ |---|---|
74
+ | La aporta el usuario | Única pero incorrecta; cambiarla después es caro |
75
+ | La genera la aplicación (UUID) | Ninguno relevante; requiere un generador fiable |
76
+ | La genera la base de datos (secuencia) | Llega tarde: el evento de creación sale sin id, y los objetos nuevos parecen iguales en colecciones |
77
+ | La asigna otro contexto | Sincronización con el origen; la más compleja |
78
+
79
+ Recomendación por defecto: identidad generada por la aplicación, **temprana** (antes de persistir), generada por el propio value object de identidad (`OrderId.generate()`), nunca por el repositorio [IDDD cap. 5 para la identidad temprana; `[Fuera de los libros: convención de @archi-code/ddd]` para quién la genera]. Si conviene orden temporal en índices, `new OrderId(uuidv7())`: `IdType` acepta cualquier versión de UUID `[Fuera de los libros]`.
80
+
81
+ ## Paso 4. Validación en tres niveles
82
+
83
+ [IDDD cap. 5; PHP: Entities]
84
+
85
+ 1. **Atributo:** en el constructor del value object (`Required(...)` y `@Validations`).
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
+ 3. **Composición:** reglas sobre varios objetos o agregados. Un servicio de dominio que recibe lo cargado o lee lo necesario con `AggregateFinder`.
88
+
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
+
91
+ ## Paso 5. Diseño flexible y Specification
92
+
93
+ Revisa el diseño con los patrones de diseño flexible (Supple Design): interfaces que revelan intención, funciones sin efectos secundarios, aserciones, contornos conceptuales, clases autónomas y clausura de operaciones [IDDD cap. 5 y cap. 6 aplican varios]. Cuando una regla tiene nombre en el negocio, se usa en varios sitios o se combina con otras, modélala como **Specification** (`Specification<T>` de `@archi-code/ddd/domain`) para validar, seleccionar o construir [IDDD cap. 5; PHP: Repositories]. Guía y código en `ddd-skills/ddd-value-objects-entidades/references/specification-y-diseno-flexible.md`.
94
+
95
+ ## Paso 6. Persistencia sin fugas
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(...)`, 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
+
99
+ ## Señales de alarma
100
+
101
+ * Clases con todos los campos públicos y `get`/`set` por cada uno: modelo anémico [IDDD cap. 1].
102
+ * `amount: number` para dinero, `email: string`, `status: string`.
103
+ * Métodos `update(dto)` que copian campos del DTO a la entidad.
104
+ * Entidades hijas con id propio que nadie busca por ese id (deberían ser valores).
105
+ * `equals` que compara ids `undefined` de objetos aún no guardados [IDDD cap. 5].
106
+ * Clases base propias (`ValueObject`, `Entity`, `deepEqual`) escritas en el proyecto: vienen del paquete. Tampoco factorías `of()`: un value object se crea con `new`.
107
+ * Getters calculados (`get total()`) en agregados, entidades o composites: rompen `toPrimitives()`; escribe un método.
108
+ * Un value object con un método que modifica una entidad recibida por parámetro [IDDD cap. 6].
109
+ * Validaciones de negocio repetidas en controlador, DTO y entidad, con criterios distintos.
110
+
111
+ ## Ejemplo
112
+
113
+ `ddd-skills/ddd-value-objects-entidades/references/ejemplo-ecommerce.md` diseña `Money`, `Quantity`, `OrderLine`, `OrderLines`, `ShippingAddress` y `OrderStatus`, y explica por qué `Order` es una entidad (la clase completa, con sus límites de agregado, está en `ddd-skills/ddd-agregados/references/implementacion-ts.md`; los límites se deciden en `ddd-skills/ddd-agregados/GUIA.md`).
114
+
115
+ ## Siguiente guía
116
+
117
+ * Dónde termina cada objeto y qué se guarda junto → `ddd-skills/ddd-agregados/GUIA.md`.
118
+ * Cómo se guardan → `ddd-skills/ddd-repositorios-persistencia/GUIA.md`.
@@ -0,0 +1,87 @@
1
+ # Ejemplo: Ventas en la tienda online
2
+
3
+ Objetos del contexto de Ventas, con la decisión razonada. Los límites de agregado se deciden en `ddd-skills/ddd-agregados/GUIA.md`; aquí solo se decide valor o entidad y su comportamiento.
4
+
5
+ | Concepto | Decisión | Razón | Con el paquete |
6
+ |---|---|---|---|
7
+ | `Money` | Value object | Cuantifica; se reemplaza; igualdad por valor | `Composite({ amount: Cents, currency: Currency })` |
8
+ | `Quantity` | Value object | Regla: entero positivo | `Required(NumberType)` + `@Validations` |
9
+ | `Sku` | Value object | Identifica un producto de otro contexto: integración minimalista | `Required(StringType)` |
10
+ | `OrderLine` | Value object | Dentro del pedido se identifica por su `Sku`; nadie la busca por id propio | `Composite` |
11
+ | `OrderLines` | Value object | Reglas de la colección: al menos una línea y un SKU por línea; `total()` no suma monedas distintas | `Composite({ items: [OrderLine] })` |
12
+ | `ShippingAddress` | Value object | Describe a dónde va este pedido; si el cliente cambia de casa, el pedido no cambia | `Composite` |
13
+ | `OrderStatus` | Tipo estándar con comportamiento | Reglas de transición en un solo sitio | `Required(StatusType)`, `StatusType extends EnumType<Status>` |
14
+ | `Order` | Entidad | Ciclo de vida largo, identidad propia, cambia de estado | `AggregateRoot<OrderId, OrderEvent>` |
15
+
16
+ `Money`, `Quantity`, `Sku`, `OrderLine`, `OrderLines`, `OrderStatus`, `OrderId` y `CustomerId` están en `ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md`. Aquí, la dirección:
17
+
18
+ ```ts
19
+ import { Validations } from '@archi-code/validation';
20
+ import { Composite, Required, StringType } from '@archi-code/ddd/domain';
21
+
22
+ export class Street extends Required(StringType) {}
23
+ export class City extends Required(StringType) {}
24
+
25
+ @Validations([{ validator: 'isISO31661Alpha2' }])
26
+ export class Country extends Required(StringType) {
27
+ protected filter(value: unknown): string {
28
+ return super.filter(value).trim().toUpperCase();
29
+ }
30
+ }
31
+
32
+ export class ShippingAddress extends Composite({ street: Street, city: City, country: Country }) {}
33
+ ```
34
+
35
+ La entidad es el `Order` de `ddd-skills/ddd-agregados/references/implementacion-ts.md`, donde está la clase completa con sus eventos y su reloj; no se redefine aquí. Lo que importa para decidir valor o entidad:
36
+
37
+ ```ts
38
+ export class Order extends AggregateRoot<OrderId, OrderEvent> {
39
+ private constructor(
40
+ id: OrderId,
41
+ readonly customerId: CustomerId,
42
+ private _lines: OrderLines,
43
+ readonly shippingAddress: ShippingAddress,
44
+ private _status: OrderStatus,
45
+ private _stockReservation: ReservationId | null,
46
+ private _paymentAuthorization: PaymentAuthorization | null,
47
+ ) {
48
+ super(id);
49
+ }
50
+
51
+ addLine(line: OrderLine, clock: { now(): Date }): void {
52
+ if (!this._status.isPending()) {
53
+ throw new OrderNoLongerEditable(this.id.value);
54
+ }
55
+ this._lines = this._lines.withLine(line);
56
+ this.record(
57
+ new OrderLineAdded(
58
+ this.id.value,
59
+ line.sku.value,
60
+ line.quantity.value,
61
+ Order.moneyData(line.unitPrice),
62
+ Order.moneyData(this.total()),
63
+ { clock },
64
+ ),
65
+ );
66
+ }
67
+
68
+ total(): Money {
69
+ return this._lines.total();
70
+ }
71
+ }
72
+ ```
73
+
74
+ La entidad apenas tiene lógica propia: delega en sus valores (`OrderLines.total`, `OrderLines.withLine`, `OrderStatus.isPending`). Es lo que IDDD llama orientar la entidad a contenedor de valores [IDDD cap. 6]. `addLine` reemplaza el `OrderLines` entero por el que devuelve `withLine`; nunca cambia una línea por dentro.
75
+
76
+ El pedido referencia al cliente por su id (`customerId: CustomerId`), nunca por el objeto. Los eventos que registra (`OrderPlaced`, `OrderLineAdded`…) y el reloj que recibe cada método se explican en `ddd-skills/ddd-eventos-dominio/GUIA.md`.
77
+
78
+ Un test de ida y vuelta por agregado comprueba que se puede guardar y rehacer `[Fuera de los libros: convención de @archi-code/ddd]`:
79
+
80
+ ```ts
81
+ import { expect, it } from 'vitest';
82
+
83
+ it('rebuilds the order it stored', () => {
84
+ const order = Order.place(OrderId.generate(), CustomerId.generate(), lines, address, clock);
85
+ expect(Order.fromPrimitives(order.toPrimitives()).toPrimitives()).toEqual(order.toPrimitives());
86
+ });
87
+ ```
@@ -0,0 +1,218 @@
1
+ # Entidades en TypeScript
2
+
3
+ ## Qué es una entidad
4
+
5
+ Un concepto que se modela como entidad cuando importa su individualidad: hay que distinguirlo de todos los demás y puede cambiar mucho durante mucho tiempo sin dejar de ser el mismo. Identidad única y mutabilidad son lo que la separa de un value object [IDDD cap. 5].
6
+
7
+ Antes de crear una, comprueba si el concepto es en realidad un valor. Y si un sistema solo edita datos, un CRUD puede ser la herramienta adecuada; el problema es aplicar CRUD a sistemas complejos que merecen DDD [IDDD cap. 5].
8
+
9
+ ## Descubrir la entidad
10
+
11
+ * Empieza solo por los atributos que dan identidad y los que sirven para encontrarla; ignora el resto hasta tener eso claro [IDDD cap. 5].
12
+ * Luego busca el comportamiento esencial en los requisitos y nómbralo con el lenguaje. En IDDD, "los tenants pueden estar activos o desactivados" lleva a `activate()` y `deactivate()`, no a `setActive(boolean)` [IDDD cap. 5].
13
+ * El lenguaje no son solo sustantivos: adjetivos describen y verbos indican lo que las cosas hacen [IDDD cap. 5].
14
+
15
+ ## Identidad
16
+
17
+ ### Estrategias [IDDD cap. 5; PHP: Entities]
18
+
19
+ | Estrategia | Cuándo | Cuidado |
20
+ |---|---|---|
21
+ | La aporta el usuario | El negocio exige un identificador legible (código de campaña) | Puede ser única pero incorrecta. Alternativa: guardar el valor del usuario como atributo para búsquedas y usar otra identidad |
22
+ | La genera la aplicación | Opción por defecto | Generador fiable también en despliegues con varios nodos |
23
+ | La genera la persistencia | Secuencias heredadas | Llega tarde (ver abajo); coste de ir a la base por cada id |
24
+ | La asigna otro contexto | El concepto nace fuera (un cliente del CRM) | Sincronización por eventos; úsala lo menos posible |
25
+
26
+ Con el paquete, la identidad generada por la aplicación es un `IdType` (`class OrderId extends IdType<'OrderId'> {}`); la que aporta el usuario o asigna otro contexto, un `Required(StringType)` con nombre y sus reglas (`CampaignCode`, en `ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md`, sección 3), usado como `AggregateRoot<CampaignCode>`.
27
+
28
+ ### Momento: temprana o tardía
29
+
30
+ * **Tardía** (al persistir): el evento de creación se publicaría sin identidad y, si `equals` compara ids, varios objetos nuevos parecerían el mismo en un `Set` [IDDD cap. 5].
31
+ * **Temprana** (al construir): el id se genera antes de crear la entidad y se pasa al constructor [IDDD cap. 5]. IDDD prefiere esta opción frente a hacer `equals` por atributos.
32
+ * Lo genera el propio value object de identidad, nunca el repositorio: el repositorio solo obtiene y persiste, sin `nextId()` `[Fuera de los libros: convención de @archi-code/ddd]`.
33
+
34
+ ```ts
35
+ import { IdType } from '@archi-code/ddd/domain';
36
+
37
+ export class OrderId extends IdType<'OrderId'> {}
38
+
39
+ const order = Order.place(OrderId.generate(), customerId, lines, shippingAddress, clock);
40
+ ```
41
+
42
+ `XId.generate()` crea un value object de la clase sobre la que se llama (`OrderId.generate()` es un `OrderId`) con un UUID v4. Si conviene orden temporal en índices, `new OrderId(uuidv7())` `[Fuera de los libros]`.
43
+
44
+ ### Identidad sustituta
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`, que también es solo del repositorio), nunca dentro de `toPrimitives()` ni como campo del agregado.
47
+
48
+ ### Estabilidad
49
+
50
+ La identidad no cambia durante la vida de la entidad. IDDD la protege con un setter interno que solo permite asignarla una vez [IDDD cap. 5]. Con el paquete, el id se pasa a `super(id)` y queda como `readonly id`; no se redeclara (`_id` + getter). Además es siempre un value object obligatorio (`IdType` o `Required(...)`): un id sin valor lanza `RequiredValueException`, así que dos entidades nuevas nunca son iguales por un id vacío [IDDD cap. 5].
51
+
52
+ ## Patrón de implementación
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 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
+
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
+
58
+ ```ts
59
+ import { BooleanType, DomainException, Entity, NumberType, Required, StringType } from '@archi-code/ddd/domain';
60
+
61
+ export class SeatNumber extends Required(NumberType) {}
62
+ export class SeatLabel extends Required(StringType) {}
63
+ export class Sold extends Required(BooleanType) {}
64
+
65
+ export class Seat extends Entity<SeatNumber> {
66
+ constructor(
67
+ id: SeatNumber,
68
+ private _label: SeatLabel,
69
+ readonly sold: Sold,
70
+ ) {
71
+ super(id);
72
+ }
73
+
74
+ get label(): SeatLabel {
75
+ return this._label;
76
+ }
77
+
78
+ relabel(label: SeatLabel): void {
79
+ if (this.sold.value) {
80
+ throw new DomainException(`Seat ${this.id.value} is already sold`, ['ROOM001']);
81
+ }
82
+ this._label = label;
83
+ }
84
+ }
85
+
86
+ new Seat(new SeatNumber(7), new SeatLabel('A1'), new Sold(false)).equals(
87
+ new Seat(new SeatNumber(7), new SeatLabel('B7'), new Sold(true)),
88
+ ); // true: misma clase y mismo id
89
+ ```
90
+
91
+ Si nadie busca la parte por su id (las líneas de un pedido), no es una entidad hija sino un `Composite` (`ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md`, sección 7).
92
+
93
+ ### Creación frente a reconstitución
94
+
95
+ Separar dos caminos evita que la carga desde base de datos vuelva a disparar reglas de creación o eventos `[Fuera de los libros como patrón explícito; IDDD lo resuelve con acceso por campo del ORM]`. Con el paquete, los dos caminos son una factoría de negocio y `static fromPrimitives(primitives: PrimitiveTypes<X>)`, con su `toPrimitives()` para guardar `[Fuera de los libros: convención de @archi-code/ddd]`:
96
+
97
+ ```ts
98
+ import { Validations } from '@archi-code/validation';
99
+ import { AggregateRoot, DomainException, EnumType, EventBase, Required, StringType } from '@archi-code/ddd/domain';
100
+ import type { EventMetadata, PrimitiveTypes } from '@archi-code/ddd/domain';
101
+
102
+ export class CustomerName extends Required(StringType) {}
103
+
104
+ @Validations([{ validator: 'isLength', properties: { min: 3, max: 200 } }])
105
+ export class BlockReason extends Required(StringType) {}
106
+
107
+ export enum CustomerState {
108
+ Active = 'active',
109
+ Blocked = 'blocked',
110
+ }
111
+
112
+ export class CustomerStateType extends EnumType<CustomerState> {
113
+ protected getEnum(): typeof CustomerState {
114
+ return CustomerState;
115
+ }
116
+ }
117
+
118
+ export class CustomerStatus extends Required(CustomerStateType) {
119
+ isBlocked(): boolean {
120
+ return this.value === CustomerState.Blocked;
121
+ }
122
+ }
123
+
124
+ export class CustomerBlocked extends EventBase {
125
+ static readonly eventName = 'CustomerBlocked';
126
+
127
+ constructor(
128
+ readonly customerId: string,
129
+ readonly reason: string,
130
+ metadata?: EventMetadata,
131
+ ) {
132
+ super(metadata);
133
+ }
134
+ }
135
+
136
+ export class BlockedCustomerCannotChangeEmail extends DomainException {
137
+ constructor(customerId: string) {
138
+ super(`Customer ${customerId} is blocked and cannot change the email`, ['CUS001']);
139
+ }
140
+ }
141
+
142
+ export class Customer extends AggregateRoot<CustomerId, CustomerBlocked> {
143
+ private constructor(
144
+ id: CustomerId,
145
+ private _name: CustomerName,
146
+ private _email: Email,
147
+ private _status: CustomerStatus,
148
+ ) {
149
+ super(id);
150
+ }
151
+
152
+ static register(id: CustomerId, name: CustomerName, email: Email): Customer {
153
+ return new Customer(id, name, email, new CustomerStatus(CustomerState.Active));
154
+ }
155
+
156
+ static fromPrimitives(primitives: PrimitiveTypes<Customer>): Customer {
157
+ return new Customer(
158
+ new CustomerId(primitives.id),
159
+ new CustomerName(primitives.name),
160
+ new Email(primitives.email),
161
+ new CustomerStatus(primitives.status),
162
+ );
163
+ }
164
+
165
+ get name(): CustomerName {
166
+ return this._name;
167
+ }
168
+
169
+ get email(): Email {
170
+ return this._email;
171
+ }
172
+
173
+ get status(): CustomerStatus {
174
+ return this._status;
175
+ }
176
+
177
+ changeEmail(email: Email): void {
178
+ if (this._status.isBlocked()) {
179
+ throw new BlockedCustomerCannotChangeEmail(this.id.value);
180
+ }
181
+ this._email = email;
182
+ }
183
+
184
+ block(reason: BlockReason, clock: { now(): Date }): void {
185
+ if (this._status.isBlocked()) {
186
+ return;
187
+ }
188
+ this._status = new CustomerStatus(CustomerState.Blocked);
189
+ this.record(new CustomerBlocked(this.id.value, reason.value, { clock }));
190
+ }
191
+
192
+ toPrimitives(): PrimitiveTypes<Customer> {
193
+ return {
194
+ id: this.id.value,
195
+ name: this._name.value,
196
+ email: this._email.value,
197
+ status: this._status.value,
198
+ };
199
+ }
200
+ }
201
+ ```
202
+
203
+ `CustomerId` y `Email` son los de `ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md`.
204
+
205
+ Fíjate en:
206
+ * Constructor privado y dos factorías con nombre: `register` (lenguaje del negocio, puede registrar eventos) y `fromPrimitives` (técnica, solo para repositorios: rehace cada value object desde su primitivo, que vuelve a validar, y no registra nada).
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()`.
208
+ * Todos los atributos son value objects; cambiar el correo es reemplazar un `Email`.
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.
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.
211
+
212
+ ## Seguimiento de cambios
213
+
214
+ Una entidad no tiene por qué guardar su historial. Si ciertos cambios importan al negocio, la forma práctica es un evento de dominio por cada comando relevante y un almacén de eventos [IDDD cap. 5]. Ver `ddd-skills/ddd-eventos-dominio/GUIA.md`.
215
+
216
+ ## Roles
217
+
218
+ Un mismo objeto puede jugar varios roles; IDDD discute modelarlo con interfaces por rol y advierte de que se puede complicar [IDDD cap. 5]. Antes de hacerlo, revisa si los roles pertenecen a contextos distintos: suele ser el caso (Cliente en Ventas, Destinatario en Almacén).