@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,301 @@
1
+ # La transacción es del repositorio
2
+
3
+ El dominio nunca es el lugar para gestionar transacciones; sus operaciones son demasiado finas y no deben saber que existen. Los libros las sitúan en la capa de aplicación, a menudo con una fachada transaccional o de forma declarativa [IDDD cap. 12; PHP: Repositories, "Managing Transactions"].
4
+
5
+ Con el paquete la transacción baja un nivel más: es del repositorio, igual que todo lo demás de la persistencia `[Fuera de los libros: convención de @archi-code/ddd]`. La aplicación nunca abre, une ni cierra transacciones y no hay ningún puerto para hacerlo. Un caso de uso recibe solo los roles del repositorio que usa (`AggregateFinder`, `AggregateSaver`, `AggregateRemover`), el `EventBus`, el `Clock` y sus puertos propios; nunca una dependencia `transactions`, `unitOfWork` o parecida.
6
+
7
+ ## Por qué basta con el repositorio
8
+
9
+ Un caso de uso modifica un solo agregado y lo guarda una vez [IDDD cap. 10]. El agregado es el límite de consistencia, así que la "transacción por agregado" de los libros se traduce en **una escritura atómica por agregado, garantizada por el repositorio** `[Fuera de los libros: convención de @archi-code/ddd]`. No queda nada que la aplicación tenga que envolver: cargar es una lectura, y `save` o `remove` es una sola escritura atómica.
10
+
11
+ Lo que hace el adaptador del repositorio, y nadie más:
12
+
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 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
+ * La conexión, la sesión y el pool.
16
+ * Envolver los errores del driver en `InternalErrorException` con `{ cause }`.
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
+
19
+ ## Varias tablas en PostgreSQL
20
+
21
+ `[Fuera de los libros: esquema propio siguiendo IDDD cap. 6]`
22
+
23
+ Si las líneas del pedido viven en su propia tabla, guardar el pedido son varias sentencias, y el repositorio las agrupa en una transacción de la base de datos dentro de `save`. `SqlConnection` y `PgPool` describen, de forma estructural, los métodos de `pg` que usa; en `infrastructure/persistence/pg-pool.ts`:
24
+
25
+ ```ts
26
+ export interface SqlConnection {
27
+ query<Row>(text: string, values?: unknown[]): Promise<{ rows: Row[]; rowCount: number | null }>;
28
+ }
29
+
30
+ export interface PgPool extends SqlConnection {
31
+ connect(): Promise<SqlConnection & { release(destroy?: boolean): void }>;
32
+ }
33
+ ```
34
+
35
+ ```sql
36
+ CREATE TABLE orders (
37
+ id UUID PRIMARY KEY,
38
+ version INT NOT NULL,
39
+ customer_id UUID NOT NULL,
40
+ status TEXT NOT NULL,
41
+ data JSONB NOT NULL
42
+ );
43
+
44
+ CREATE TABLE order_lines (
45
+ order_id UUID NOT NULL REFERENCES orders (id) ON DELETE CASCADE,
46
+ position INT NOT NULL,
47
+ sku TEXT NOT NULL,
48
+ quantity INT NOT NULL,
49
+ unit_price JSONB NOT NULL,
50
+ PRIMARY KEY (order_id, position)
51
+ );
52
+ ```
53
+
54
+ En `infrastructure/persistence/postgres-order-repository.ts`:
55
+
56
+ ```ts
57
+ import type { AggregateFinder, AggregateSaver, PrimitiveTypes } from '@archi-code/ddd/domain';
58
+ import { ConcurrencyException } from '@archi-code/ddd/application';
59
+ import { InternalErrorException } from '@archi-code/ddd/infrastructure';
60
+ import { Order, OrderId } from '../../domain/order/order';
61
+ import type { PgPool } from './pg-pool';
62
+
63
+ type OrderPrimitives = PrimitiveTypes<Order>;
64
+
65
+ interface OrderRow {
66
+ version: number;
67
+ data: Omit<OrderPrimitives, 'lines'>;
68
+ items: OrderPrimitives['lines']['items'];
69
+ }
70
+
71
+ export class PostgresOrderRepository implements AggregateFinder<Order>, AggregateSaver<Order> {
72
+ private readonly versions = new WeakMap<Order, number>();
73
+
74
+ constructor(private readonly pool: PgPool) {}
75
+
76
+ async findById(id: OrderId): Promise<Order | null> {
77
+ const { rows } = await this.pool.query<OrderRow>(
78
+ `SELECT o.version, o.data,
79
+ COALESCE(
80
+ json_agg(
81
+ json_build_object('sku', l.sku, 'quantity', l.quantity, 'unitPrice', l.unit_price)
82
+ ORDER BY l.position
83
+ ) FILTER (WHERE l.order_id IS NOT NULL),
84
+ '[]'
85
+ ) AS items
86
+ FROM orders o
87
+ LEFT JOIN order_lines l ON l.order_id = o.id
88
+ WHERE o.id = $1
89
+ GROUP BY o.id`,
90
+ [id.value],
91
+ );
92
+ const row = rows[0];
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;
99
+ }
100
+
101
+ async save(order: Order): Promise<void> {
102
+ const version = this.versions.get(order) ?? 0;
103
+ const { lines, ...data } = order.toPrimitives();
104
+ const client = await this.pool.connect();
105
+ let broken = false;
106
+ try {
107
+ await client.query('BEGIN');
108
+ const { rowCount } =
109
+ version === 0
110
+ ? await client.query(
111
+ 'INSERT INTO orders (id, version, customer_id, status, data) VALUES ($1, 1, $2, $3, $4) ON CONFLICT (id) DO NOTHING',
112
+ [data.id, data.customerId, data.status, data],
113
+ )
114
+ : await client.query(
115
+ 'UPDATE orders SET version = version + 1, status = $3, data = $4 WHERE id = $1 AND version = $2',
116
+ [data.id, version, data.status, data],
117
+ );
118
+ if (rowCount === 0) {
119
+ throw new ConcurrencyException('Order', order.id.value, version);
120
+ }
121
+ await client.query('DELETE FROM order_lines WHERE order_id = $1', [data.id]);
122
+ for (const [position, line] of lines.items.entries()) {
123
+ await client.query(
124
+ 'INSERT INTO order_lines (order_id, position, sku, quantity, unit_price) VALUES ($1, $2, $3, $4, $5)',
125
+ [data.id, position, line.sku, line.quantity, line.unitPrice],
126
+ );
127
+ }
128
+ await client.query('COMMIT');
129
+ } catch (error) {
130
+ await client.query('ROLLBACK').catch(() => {
131
+ broken = true;
132
+ });
133
+ if (error instanceof ConcurrencyException) {
134
+ throw error;
135
+ }
136
+ throw new InternalErrorException(`writing order ${order.id.value}`, { cause: error });
137
+ } finally {
138
+ client.release(broken);
139
+ }
140
+ this.versions.set(order, version + 1);
141
+ }
142
+ }
143
+ ```
144
+
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.
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`.
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.
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.
149
+ * La lectura es una sola sentencia, así que ve el pedido y sus líneas en el mismo instante.
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`).
151
+
152
+ Con MongoDB no hace falta nada de esto: el pedido es un documento y `replaceOne` lo escribe de forma atómica (`MongoOrderRepository` en `ddd-skills/ddd-repositorios-persistencia/references/implementacion-ts.md`).
153
+
154
+ ## Eventos y guardado
155
+
156
+ El caso de uso guarda y después publica: `await this.orders.save(order)` y `await this.events.publish(order.pullDomainEvents())`. Si `publish` falla después de `save`, el cambio queda guardado y el evento puede perderse.
157
+
158
+ Cuando un evento no puede perderse, el repositorio escribe el agregado y sus eventos pendientes en la misma escritura atómica (una bandeja de salida, *outbox*, dentro del mismo documento o en la misma transacción de `save`), y un proceso aparte (*relay*) los entrega después a través de un `EventChannel` `[Fuera de los libros: convención de @archi-code/ddd]`. En el repositorio de PostgreSQL de arriba es una sentencia más antes del `COMMIT`:
159
+
160
+ ```ts
161
+ for (const event of order.pullDomainEvents()) {
162
+ await client.query('INSERT INTO order_outbox (event_id, body) VALUES ($1, $2)', [
163
+ event.eventId,
164
+ { ...event },
165
+ ]);
166
+ }
167
+ ```
168
+
169
+ El repositorio toma los eventos con `pullDomainEvents()`, así que el `publish` posterior del caso de uso recibe una lista vacía y no envía nada: los casos de uso no cambian. Es persistencia, así que pertenece al adaptador del repositorio.
170
+
171
+ ## Uso en un caso de uso
172
+
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.
174
+
175
+ ```ts
176
+ import { AggregateNotFoundException, ConcurrencyException, UseCase, ValueObjectBuilder } from '@archi-code/ddd/application';
177
+ import type { Clock, EventBus } from '@archi-code/ddd/application';
178
+ import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
179
+ import { Order, OrderId, OrderLine } from '../../domain/order/order';
180
+ import type { OrderEvent } from '../../domain/order/order';
181
+
182
+ export interface AddOrderLineInput {
183
+ orderId: string;
184
+ sku: string;
185
+ quantity: number;
186
+ unitPrice: { amount: number; currency: string };
187
+ }
188
+
189
+ interface AddOrderLineCommand {
190
+ orderId: OrderId;
191
+ line: OrderLine;
192
+ }
193
+
194
+ const ATTEMPTS = 3;
195
+
196
+ export class AddOrderLine extends UseCase<AddOrderLineInput, AddOrderLineCommand, void> {
197
+ constructor(
198
+ private readonly orders: AggregateFinder<Order> & AggregateSaver<Order>,
199
+ private readonly events: EventBus<OrderEvent>,
200
+ private readonly clock: Clock,
201
+ ) {
202
+ super();
203
+ }
204
+
205
+ protected toCommand(input: AddOrderLineInput): AddOrderLineCommand {
206
+ return ValueObjectBuilder.build({
207
+ orderId: () => new OrderId(input.orderId),
208
+ line: () =>
209
+ OrderLine.fromPrimitives({
210
+ sku: input.sku,
211
+ quantity: input.quantity,
212
+ unitPrice: input.unitPrice,
213
+ }),
214
+ });
215
+ }
216
+
217
+ protected async handle(command: AddOrderLineCommand): Promise<void> {
218
+ for (let attempt = 1; ; attempt += 1) {
219
+ const order = await this.orders.findById(command.orderId);
220
+ if (order === null) {
221
+ throw new AggregateNotFoundException('Order', command.orderId.value);
222
+ }
223
+ order.addLine(command.line, this.clock);
224
+ try {
225
+ await this.orders.save(order);
226
+ } catch (error) {
227
+ if (error instanceof ConcurrencyException && attempt < ATTEMPTS) {
228
+ continue;
229
+ }
230
+ throw error;
231
+ }
232
+ await this.events.publish(order.pullDomainEvents());
233
+ return;
234
+ }
235
+ }
236
+ }
237
+ ```
238
+
239
+ `AggregateNotFoundException` la lanza el caso de uso, no el repositorio: `findById` devuelve `null`. Solo se reintenta el `save`: un error de `publish` no vuelve a guardar el pedido.
240
+
241
+ ## En tests
242
+
243
+ Los tests de la aplicación usan el `InMemoryOrderRepository` que el proyecto escribe en `test/support/` (`ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md`) y un `DomainEventBus` sobre `InMemoryEventChannel`; no hay nada que revertir ni que contar, porque cada `save` del repositorio en memoria es atómico y comprueba la versión igual que uno real. En `test/contexts/sales/application/add-order-line.spec.ts`:
244
+
245
+ ```ts
246
+ import { describe, expect, it } from 'vitest';
247
+ import {
248
+ DomainEventBus,
249
+ EventRegistry,
250
+ FixedClock,
251
+ InMemoryEventChannel,
252
+ } from '@archi-code/ddd/infrastructure';
253
+ import { AddOrderLine } from '../../../../src/contexts/sales/application/add-order-line/add-order-line';
254
+ import {
255
+ CustomerId,
256
+ Order,
257
+ OrderId,
258
+ OrderLines,
259
+ ShippingAddress,
260
+ } from '../../../../src/contexts/sales/domain/order/order';
261
+ import type { OrderEvent } from '../../../../src/contexts/sales/domain/order/order';
262
+ import { InMemoryOrderRepository } from '../../../support/in-memory-order-repository';
263
+
264
+ describe('Two customers add a line to the same order at once', () => {
265
+ it('the second one retries with the reloaded order and both lines are kept', async () => {
266
+ const clock = new FixedClock('2026-03-01T09:30:00Z');
267
+ const orders = new InMemoryOrderRepository();
268
+ const channel = new InMemoryEventChannel();
269
+ const addOrderLine = new AddOrderLine(
270
+ orders,
271
+ new DomainEventBus<OrderEvent>(channel, new EventRegistry<OrderEvent>()),
272
+ clock,
273
+ );
274
+ const order = Order.place(
275
+ OrderId.generate(),
276
+ CustomerId.generate(),
277
+ OrderLines.fromPrimitives({
278
+ items: [{ sku: 'TORCH-01', quantity: 2, unitPrice: { amount: 4990, currency: 'EUR' } }],
279
+ }),
280
+ ShippingAddress.fromPrimitives({ street: 'Calle Mayor 1', city: 'Huelva', country: 'ES' }),
281
+ clock,
282
+ );
283
+ await orders.save(order);
284
+ const unitPrice = { amount: 1990, currency: 'EUR' };
285
+
286
+ await Promise.all([
287
+ addOrderLine.execute({ orderId: order.id.value, sku: 'HELMET-02', quantity: 1, unitPrice }),
288
+ addOrderLine.execute({ orderId: order.id.value, sku: 'GLOVES-03', quantity: 1, unitPrice }),
289
+ ]);
290
+
291
+ const stored = await orders.findById(order.id);
292
+ expect(stored?.toPrimitives().lines.items).toHaveLength(3);
293
+ expect(channel.sent).toMatchObject([
294
+ { eventName: 'OrderLineAdded', sku: 'HELMET-02' },
295
+ { eventName: 'OrderLineAdded', sku: 'GLOVES-03' },
296
+ ]);
297
+ });
298
+ });
299
+ ```
300
+
301
+ Los dos casos de uso cargan el pedido en la versión 1; el primero guarda, el segundo recibe `ConcurrencyException`, vuelve a cargar y guarda sobre la versión 2. La atomicidad de un repositorio real (sus varias tablas, su outbox) se prueba en su propio spec, con la suite de contrato de `ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md` contra la base de datos de verdad.
@@ -0,0 +1,99 @@
1
+ # Servicios de dominio y capa de aplicación
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.** Servicios de dominio y servicios de aplicación (casos de uso) en DDD con TypeScript. Cubre cuándo una operación merece un servicio de dominio y cuándo pertenece a una entidad o value object, cómo evitar el modelo anémico, cómo escribir casos de uso finos (cargar, invocar, guardar, publicar), comandos y DTOs de entrada, salida mediante DTOs o read models, command bus, dónde queda la transacción (en el repositorio), seguridad y autorización, manejo de errores y tests de la capa de aplicación con adaptadores en memoria. Consúltala siempre que se escriba o revise un "service", "manager", "handler", "use case" o "interactor", cuando la lógica de negocio esté en servicios en vez de en el modelo, al decidir dónde va una regla que implica varios objetos, o al conectar un controlador con el dominio, 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 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
+ * 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
+ * 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 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
+ * 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
+ * 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
+
18
+ Dos cosas con el mismo nombre ("servicio") y papeles opuestos. Confundirlas es la vía más rápida hacia un modelo anémico [IDDD cap. 7 y cap. 14].
19
+
20
+ | | Servicio de dominio | Servicio de aplicación |
21
+ |---|---|---|
22
+ | Contiene lógica de negocio | **Sí** | **No** |
23
+ | Vive en | `domain/` | `application/` |
24
+ | Nombre | Del lenguaje ubicuo | Del caso de uso |
25
+ | Estado | Sin estado [IDDD cap. 7] | Sin estado; coordina |
26
+ | Transacciones y seguridad | Nunca [IDDD cap. 7] | Seguridad sí; transacciones según IDDD sí [IDDD cap. 14], con el paquete las garantiza el repositorio `[Fuera de los libros: convención de @archi-code/ddd]` |
27
+ | Quién lo llama | Servicios de aplicación (u otros de dominio) | Adaptadores de entrada (HTTP, mensajes, CLI) |
28
+
29
+ Ninguno es un servicio SOA remoto ni tiene por qué ser pesado [IDDD cap. 7].
30
+
31
+
32
+ ## Paso 1. ¿Dónde va esta lógica?
33
+
34
+ Árbol de decisión `[Fuera de los libros: síntesis de IDDD cap. 7, cap. 10 y cap. 14]`:
35
+
36
+ 1. ¿Es una regla sobre el estado de **un** agregado? → método del agregado.
37
+ 2. ¿Es un cálculo o comportamiento sobre unos valores? → método del value object.
38
+ 3. ¿Es un proceso de negocio relevante, una transformación de una composición a otra, o un cálculo que necesita **varios** objetos de dominio y queda forzado en cualquiera de ellos? → **servicio de dominio** [IDDD cap. 7].
39
+ 4. ¿Es coordinación: cargar, decidir qué invocar, guardar, publicar, autorizar? → **servicio de aplicación** [IDDD cap. 14].
40
+ 5. ¿Es formato, protocolo o traducción de otro sistema? → adaptador o ACL.
41
+
42
+ Si la "lógica" es una regla con nombre que decide si algo cumple un criterio (cliente moroso, pedido apto para envío urgente), suele ser una **Specification**, no un servicio (ver `ddd-skills/ddd-value-objects-entidades/GUIA.md`).
43
+
44
+ Antes de crear un servicio de dominio, asegúrate de que lo necesitas. Usarlos por defecto lleva al modelo anémico [IDDD cap. 7]. IDDD admite una "mini capa" de servicios de dominio en algunos contextos, pero avisa de que es un camino peligroso [IDDD cap. 7].
45
+
46
+ ## Paso 2. Servicios de dominio
47
+
48
+ * Interfaz con el lenguaje del modelo; el nombre de la operación forma parte del lenguaje ubicuo; sin estado [IDDD cap. 7, citando a Evans].
49
+ * Reciben y devuelven objetos de dominio, no DTOs ni primitivos.
50
+ * Algunos tienen implementación técnica en infraestructura, sobre todo los de integración, con interfaz en el dominio (ver la ACL en `ddd-skills/ddd-context-mapping/GUIA.md`) [IDDD cap. 7].
51
+ * Pueden leer agregados por repositorio para cálculos o validaciones de composición [IDDD cap. 5 y cap. 7], pero **no modifican varios agregados** en una misma operación. Con el paquete, el servicio lee con `AggregateFinder` (el contrato del dominio) o el caso de uso le pasa los agregados ya cargados; nunca recibe `AggregateSaver` ni `AggregateRemover`.
52
+ * El paquete no trae clase base: un servicio de dominio es una clase normal; una regla con nombre que decide si algo cumple un criterio es una `Specification<T>` de `@archi-code/ddd/domain`.
53
+
54
+ Ejemplos e implementación en `ddd-skills/ddd-servicios-capa-aplicacion/references/servicios-dominio-ts.md`.
55
+
56
+ ## Paso 3. Servicios de aplicación
57
+
58
+ * Clientes directos del modelo; un método por flujo de caso de uso; según IDDD controlan transacciones y seguridad [IDDD cap. 14]. Con el paquete la seguridad sigue en el caso de uso, pero la transacción pasa al repositorio `[Fuera de los libros: convención de @archi-code/ddd]`.
59
+ * **Finos.** Si crecen, la lógica de dominio se escapa y el modelo se vuelve anémico [IDDD cap. 4 y cap. 14; PHP: Application].
60
+ * Forma típica: recibir parámetros, obtener el agregado del repositorio, ejecutar un comando sobre él, guardar [IDDD cap. 4].
61
+ * Entrada con primitivos, DTOs o, mejor, objetos comando [PHP: Application, citando a Vernon]. Con el paquete, un `Input` plano de primitivos y un `Command` de value objects que construye `toCommand` `[Fuera de los libros: convención de @archi-code/ddd]`.
62
+ * Salida: identificador, DTO de lectura, o nada (con un puerto de salida), según lo que necesite el cliente [IDDD cap. 14]. Es el tipo `Output` de `UseCase`; nunca el agregado.
63
+ * Transacciones: IDDD las pone en el servicio de aplicación [IDDD cap. 14]. Con el paquete la aplicación nunca abre, une ni cierra transacciones y no hay puerto para ello: el adaptador del repositorio es responsable de todo lo de persistencia (que cada `save`/`remove` sea atómico, la comprobación de versión con `ConcurrencyException`, la conexión o sesión, envolver los errores del driver en `InternalErrorException` con `{ cause }`). "Una transacción por agregado" se traduce en "una escritura atómica por agregado, garantizada por el repositorio" `[Fuera de los libros: convención de @archi-code/ddd]`.
64
+
65
+ Plantilla, variantes y errores frecuentes en `ddd-skills/ddd-servicios-capa-aplicacion/references/capa-aplicacion-ts.md`.
66
+
67
+ ## Paso 4. Seguridad
68
+
69
+ * La autorización del caso de uso ("¿puede este usuario cancelar pedidos?") es responsabilidad de la aplicación [IDDD cap. 14]: el caso de uso consulta un puerto de aplicación propio (`SalesPermissions`) y lanza `NotAllowedException` (403).
70
+ * Si la regla es de negocio ("solo el cliente que hizo el pedido puede cancelarlo"), pertenece al modelo y se expresa con conceptos del contexto (`Customer`, no `User`) [IDDD cap. 2].
71
+ * La identidad del solicitante viene del token en el adaptador, nunca del cuerpo de la petición; el adaptador la pone en el `Input` y `toCommand` la convierte en value object.
72
+ * La autenticación (quién es el solicitante) la resuelve la plataforma o el adaptador de entrada según las normas del proyecto; esta guía solo cubre dónde encaja la autorización en la arquitectura.
73
+
74
+ ## Paso 5. Probar
75
+
76
+ * Servicios de dominio: tests unitarios con objetos de dominio reales [IDDD cap. 7].
77
+ * Servicios de aplicación: con repositorios y adaptadores **en memoria**; comprueban orquestación, no reglas de negocio [IDDD cap. 12; PHP: Application]. El repositorio en memoria lo escribe el proyecto en `test/support/`, uno por agregado (`InMemoryOrderRepository`, como en `ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md`); del paquete: `DomainEventBus` sobre `InMemoryEventChannel` (`channel.sent`) y `FixedClock`.
78
+ * Las reglas de negocio se prueban en el agregado o el value object, no a través del caso de uso.
79
+
80
+ ## Señales de alarma
81
+
82
+ * `OrderService` con 40 métodos y entidades con solo getters y setters [Destilado cap. 5].
83
+ * `if` sobre el estado de una entidad dentro del servicio de aplicación.
84
+ * Servicios de dominio llamados `XxxManager`, `XxxHelper`, `XxxUtils` [Destilado cap. 5].
85
+ * Un servicio de dominio que abre transacciones o comprueba permisos de usuario.
86
+ * Un caso de uso que abre o maneja transacciones, o recibe un runner de transacciones o una unidad de trabajo: eso va dentro del adaptador del repositorio.
87
+ * Un caso de uso que guarda dos agregados.
88
+ * El caso de uso devuelve el agregado al controlador.
89
+ * Un caso de uso que importa o llama a otro caso de uso.
90
+ * Tests de reglas de negocio escritos contra el caso de uso con mocks de todo.
91
+
92
+ ## Ejemplo
93
+
94
+ `ddd-skills/ddd-servicios-capa-aplicacion/references/ejemplo-ecommerce.md`: lo que Ventas añade al `Order` de `ddd-skills/ddd-agregados/references/implementacion-ts.md` (`OrderAbove`, `Customer`, `OrderCancellation`), caso de uso `PlaceOrder`, listener `RegisterReservationOnStockReserved` con su caso de uso `RegisterReservation` y su test. El servicio de dominio `ShippingCostCalculator` está en `ddd-skills/ddd-servicios-capa-aplicacion/references/servicios-dominio-ts.md`, y `CancelOrder` y `AddOrderLine` en `ddd-skills/ddd-servicios-capa-aplicacion/references/capa-aplicacion-ts.md`.
95
+
96
+ ## Siguiente guía
97
+
98
+ * Si las lecturas se complican o hay mucho volumen → `ddd-skills/ddd-event-sourcing-cqrs/GUIA.md`.
99
+ * Para revisar un proyecto existente con todo esto → `ddd-skills/ddd-auditoria-refactorizacion/GUIA.md`.