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

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 (312) hide show
  1. package/LICENSE +46 -0
  2. package/README.md +2693 -2
  3. package/SKILL.md +104 -0
  4. package/ddd-skills/archi-code-ddd/imports.md +84 -0
  5. package/ddd-skills/archi-code-ddd/layers.md +299 -0
  6. package/ddd-skills/archi-code-ddd/patterns.md +676 -0
  7. package/ddd-skills/archi-code-ddd/what-not-to-do.md +189 -0
  8. package/ddd-skills/archi-code-ddd/where-each-class-goes.md +63 -0
  9. package/ddd-skills/ddd-agregados/GUIA.md +114 -0
  10. package/ddd-skills/ddd-agregados/references/consistencia-eventual.md +159 -0
  11. package/ddd-skills/ddd-agregados/references/ejemplo-ecommerce.md +52 -0
  12. package/ddd-skills/ddd-agregados/references/fabricas.md +196 -0
  13. package/ddd-skills/ddd-agregados/references/implementacion-ts.md +711 -0
  14. package/ddd-skills/ddd-arquitectura/GUIA.md +80 -0
  15. package/ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs +92 -0
  16. package/ddd-skills/ddd-arquitectura/references/estilos-adicionales.md +51 -0
  17. package/ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md +352 -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 +316 -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 +404 -0
  26. package/ddd-skills/ddd-context-mapping/references/estilos-integracion.md +155 -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 +471 -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 +131 -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 +526 -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 +296 -0
  38. package/ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md +177 -0
  39. package/ddd-skills/ddd-repositorios-persistencia/references/transacciones-ts.md +294 -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 +324 -0
  43. package/ddd-skills/ddd-servicios-capa-aplicacion/references/servicios-dominio-ts.md +191 -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 +220 -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 +377 -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/index.d.ts +7 -0
  58. package/dist/cjs/application/index.js +9 -0
  59. package/dist/cjs/application/ports/clock.d.ts +3 -0
  60. package/dist/cjs/application/ports/clock.js +2 -0
  61. package/dist/cjs/application/ports/logger.d.ts +8 -0
  62. package/dist/cjs/application/ports/logger.js +2 -0
  63. package/dist/cjs/application/use-case/use-case.d.ts +6 -0
  64. package/dist/cjs/application/use-case/use-case.js +24 -0
  65. package/dist/cjs/domain/aggregate/aggregate-root.d.ts +14 -0
  66. package/dist/cjs/domain/aggregate/aggregate-root.js +33 -0
  67. package/dist/cjs/domain/entity/entity.d.ts +9 -0
  68. package/dist/cjs/domain/entity/entity.js +19 -0
  69. package/dist/cjs/domain/event/event-base.d.ts +29 -0
  70. package/dist/cjs/domain/event/event-base.js +51 -0
  71. package/dist/cjs/domain/index.d.ts +10 -0
  72. package/dist/cjs/domain/index.js +24 -0
  73. package/dist/cjs/domain/primitive/primitive-types.d.ts +22 -0
  74. package/dist/cjs/domain/primitive/primitive-types.js +2 -0
  75. package/dist/cjs/domain/repository/aggregate-finder.d.ts +4 -0
  76. package/dist/cjs/domain/repository/aggregate-finder.js +2 -0
  77. package/dist/cjs/domain/repository/aggregate-remover.d.ts +4 -0
  78. package/dist/cjs/domain/repository/aggregate-remover.js +2 -0
  79. package/dist/cjs/domain/repository/aggregate-saver.d.ts +4 -0
  80. package/dist/cjs/domain/repository/aggregate-saver.js +2 -0
  81. package/dist/cjs/domain/specification/specification.d.ts +9 -0
  82. package/dist/cjs/domain/specification/specification.js +44 -0
  83. package/dist/cjs/domain/type/abstract-type.d.ts +20 -0
  84. package/dist/cjs/domain/type/abstract-type.js +69 -0
  85. package/dist/cjs/domain/type/array-type.d.ts +16 -0
  86. package/dist/cjs/domain/type/array-type.js +65 -0
  87. package/dist/cjs/domain/type/boolean-type.d.ts +4 -0
  88. package/dist/cjs/domain/type/boolean-type.js +16 -0
  89. package/dist/cjs/domain/type/composite/composite-field.d.ts +19 -0
  90. package/dist/cjs/domain/type/composite/composite-field.js +63 -0
  91. package/dist/cjs/domain/type/composite/composite.d.ts +26 -0
  92. package/dist/cjs/domain/type/composite/composite.js +55 -0
  93. package/dist/cjs/domain/type/date-type.d.ts +18 -0
  94. package/dist/cjs/domain/type/date-type.js +35 -0
  95. package/dist/cjs/domain/type/enum-type.d.ts +7 -0
  96. package/dist/cjs/domain/type/enum-type.js +30 -0
  97. package/dist/cjs/domain/type/equality/value-equality.d.ts +7 -0
  98. package/dist/cjs/domain/type/equality/value-equality.js +41 -0
  99. package/dist/cjs/domain/type/id-type.d.ts +7 -0
  100. package/dist/cjs/domain/type/id-type.js +17 -0
  101. package/dist/cjs/domain/type/immutable/immutable-date.d.ts +4 -0
  102. package/dist/cjs/domain/type/immutable/immutable-date.js +37 -0
  103. package/dist/cjs/domain/type/immutable/immutable.d.ts +4 -0
  104. package/dist/cjs/domain/type/immutable/immutable.js +19 -0
  105. package/dist/cjs/domain/type/index.d.ts +17 -0
  106. package/dist/cjs/domain/type/index.js +33 -0
  107. package/dist/cjs/domain/type/json-type.d.ts +5 -0
  108. package/dist/cjs/domain/type/json-type.js +17 -0
  109. package/dist/cjs/domain/type/number-type.d.ts +4 -0
  110. package/dist/cjs/domain/type/number-type.js +16 -0
  111. package/dist/cjs/domain/type/required/required-mark.d.ts +9 -0
  112. package/dist/cjs/domain/type/required/required-mark.js +13 -0
  113. package/dist/cjs/domain/type/required/required.d.ts +7 -0
  114. package/dist/cjs/domain/type/required/required.js +21 -0
  115. package/dist/cjs/domain/type/string-type.d.ts +4 -0
  116. package/dist/cjs/domain/type/string-type.js +16 -0
  117. package/dist/cjs/domain/type/uuid-type.d.ts +7 -0
  118. package/dist/cjs/domain/type/uuid-type.js +27 -0
  119. package/dist/cjs/domain/type/value-objects/value-objects.d.ts +16 -0
  120. package/dist/cjs/domain/type/value-objects/value-objects.js +70 -0
  121. package/dist/cjs/exceptions/abstract.exception.d.ts +23 -0
  122. package/dist/cjs/exceptions/abstract.exception.js +66 -0
  123. package/dist/cjs/exceptions/application/aggregate-not-found.exception.d.ts +5 -0
  124. package/dist/cjs/exceptions/application/aggregate-not-found.exception.js +17 -0
  125. package/dist/cjs/exceptions/application/application.exception.d.ts +6 -0
  126. package/dist/cjs/exceptions/application/application.exception.js +17 -0
  127. package/dist/cjs/exceptions/application/concurrency.exception.d.ts +5 -0
  128. package/dist/cjs/exceptions/application/concurrency.exception.js +17 -0
  129. package/dist/cjs/exceptions/application/not-allowed.exception.d.ts +5 -0
  130. package/dist/cjs/exceptions/application/not-allowed.exception.js +17 -0
  131. package/dist/cjs/exceptions/brand/exception-brand.d.ts +6 -0
  132. package/dist/cjs/exceptions/brand/exception-brand.js +29 -0
  133. package/dist/cjs/exceptions/domain/domain.exception.d.ts +6 -0
  134. package/dist/cjs/exceptions/domain/domain.exception.js +17 -0
  135. package/dist/cjs/exceptions/domain/required-value.exception.d.ts +4 -0
  136. package/dist/cjs/exceptions/domain/required-value.exception.js +21 -0
  137. package/dist/cjs/exceptions/domain/type-primitive.exception.d.ts +4 -0
  138. package/dist/cjs/exceptions/domain/type-primitive.exception.js +21 -0
  139. package/dist/cjs/exceptions/domain/validation.exception.d.ts +14 -0
  140. package/dist/cjs/exceptions/domain/validation.exception.js +23 -0
  141. package/dist/cjs/exceptions/exception-code.d.ts +15 -0
  142. package/dist/cjs/exceptions/exception-code.js +19 -0
  143. package/dist/cjs/exceptions/index.d.ts +14 -0
  144. package/dist/cjs/exceptions/index.js +30 -0
  145. package/dist/cjs/exceptions/infrastructure/event-delivery.exception.d.ts +5 -0
  146. package/dist/cjs/exceptions/infrastructure/event-delivery.exception.js +20 -0
  147. package/dist/cjs/exceptions/infrastructure/infrastructure.exception.d.ts +6 -0
  148. package/dist/cjs/exceptions/infrastructure/infrastructure.exception.js +17 -0
  149. package/dist/cjs/exceptions/infrastructure/internal-error.exception.d.ts +5 -0
  150. package/dist/cjs/exceptions/infrastructure/internal-error.exception.js +17 -0
  151. package/dist/cjs/exceptions/infrastructure/unknown-event.exception.d.ts +4 -0
  152. package/dist/cjs/exceptions/infrastructure/unknown-event.exception.js +19 -0
  153. package/dist/cjs/exceptions/received/received-value.d.ts +4 -0
  154. package/dist/cjs/exceptions/received/received-value.js +13 -0
  155. package/dist/cjs/infrastructure/clock/fixed-clock.d.ts +8 -0
  156. package/dist/cjs/infrastructure/clock/fixed-clock.js +19 -0
  157. package/dist/cjs/infrastructure/clock/system-clock.d.ts +4 -0
  158. package/dist/cjs/infrastructure/clock/system-clock.js +9 -0
  159. package/dist/cjs/infrastructure/event/domain-event-bus.d.ts +16 -0
  160. package/dist/cjs/infrastructure/event/domain-event-bus.js +60 -0
  161. package/dist/cjs/infrastructure/event/event-channel.d.ts +9 -0
  162. package/dist/cjs/infrastructure/event/event-channel.js +2 -0
  163. package/dist/cjs/infrastructure/event/event-registry.d.ts +9 -0
  164. package/dist/cjs/infrastructure/event/event-registry.js +43 -0
  165. package/dist/cjs/infrastructure/event/in-memory/emitter.d.ts +11 -0
  166. package/dist/cjs/infrastructure/event/in-memory/emitter.js +54 -0
  167. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.d.ts +16 -0
  168. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.js +51 -0
  169. package/dist/cjs/infrastructure/http/http-error-mapper.d.ts +23 -0
  170. package/dist/cjs/infrastructure/http/http-error-mapper.js +63 -0
  171. package/dist/cjs/infrastructure/index.d.ts +16 -0
  172. package/dist/cjs/infrastructure/index.js +21 -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/esm/application/event/event-bus.d.ts +4 -0
  182. package/dist/esm/application/event/event-bus.js +1 -0
  183. package/dist/esm/application/event/event-listener.d.ts +4 -0
  184. package/dist/esm/application/event/event-listener.js +2 -0
  185. package/dist/esm/application/event/listens-to.d.ts +15 -0
  186. package/dist/esm/application/event/listens-to.js +25 -0
  187. package/dist/esm/application/index.d.ts +7 -0
  188. package/dist/esm/application/index.js +3 -0
  189. package/dist/esm/application/ports/clock.d.ts +3 -0
  190. package/dist/esm/application/ports/clock.js +1 -0
  191. package/dist/esm/application/ports/logger.d.ts +8 -0
  192. package/dist/esm/application/ports/logger.js +1 -0
  193. package/dist/esm/application/use-case/use-case.d.ts +6 -0
  194. package/dist/esm/application/use-case/use-case.js +20 -0
  195. package/dist/esm/domain/aggregate/aggregate-root.d.ts +14 -0
  196. package/dist/esm/domain/aggregate/aggregate-root.js +29 -0
  197. package/dist/esm/domain/entity/entity.d.ts +9 -0
  198. package/dist/esm/domain/entity/entity.js +15 -0
  199. package/dist/esm/domain/event/event-base.d.ts +29 -0
  200. package/dist/esm/domain/event/event-base.js +47 -0
  201. package/dist/esm/domain/index.d.ts +10 -0
  202. package/dist/esm/domain/index.js +6 -0
  203. package/dist/esm/domain/primitive/primitive-types.d.ts +22 -0
  204. package/dist/esm/domain/primitive/primitive-types.js +1 -0
  205. package/dist/esm/domain/repository/aggregate-finder.d.ts +4 -0
  206. package/dist/esm/domain/repository/aggregate-finder.js +1 -0
  207. package/dist/esm/domain/repository/aggregate-remover.d.ts +4 -0
  208. package/dist/esm/domain/repository/aggregate-remover.js +1 -0
  209. package/dist/esm/domain/repository/aggregate-saver.d.ts +4 -0
  210. package/dist/esm/domain/repository/aggregate-saver.js +1 -0
  211. package/dist/esm/domain/specification/specification.d.ts +9 -0
  212. package/dist/esm/domain/specification/specification.js +40 -0
  213. package/dist/esm/domain/type/abstract-type.d.ts +20 -0
  214. package/dist/esm/domain/type/abstract-type.js +65 -0
  215. package/dist/esm/domain/type/array-type.d.ts +16 -0
  216. package/dist/esm/domain/type/array-type.js +61 -0
  217. package/dist/esm/domain/type/boolean-type.d.ts +4 -0
  218. package/dist/esm/domain/type/boolean-type.js +12 -0
  219. package/dist/esm/domain/type/composite/composite-field.d.ts +19 -0
  220. package/dist/esm/domain/type/composite/composite-field.js +59 -0
  221. package/dist/esm/domain/type/composite/composite.d.ts +26 -0
  222. package/dist/esm/domain/type/composite/composite.js +52 -0
  223. package/dist/esm/domain/type/date-type.d.ts +18 -0
  224. package/dist/esm/domain/type/date-type.js +29 -0
  225. package/dist/esm/domain/type/enum-type.d.ts +7 -0
  226. package/dist/esm/domain/type/enum-type.js +26 -0
  227. package/dist/esm/domain/type/equality/value-equality.d.ts +7 -0
  228. package/dist/esm/domain/type/equality/value-equality.js +37 -0
  229. package/dist/esm/domain/type/id-type.d.ts +7 -0
  230. package/dist/esm/domain/type/id-type.js +13 -0
  231. package/dist/esm/domain/type/immutable/immutable-date.d.ts +4 -0
  232. package/dist/esm/domain/type/immutable/immutable-date.js +33 -0
  233. package/dist/esm/domain/type/immutable/immutable.d.ts +4 -0
  234. package/dist/esm/domain/type/immutable/immutable.js +15 -0
  235. package/dist/esm/domain/type/index.d.ts +17 -0
  236. package/dist/esm/domain/type/index.js +14 -0
  237. package/dist/esm/domain/type/json-type.d.ts +5 -0
  238. package/dist/esm/domain/type/json-type.js +13 -0
  239. package/dist/esm/domain/type/number-type.d.ts +4 -0
  240. package/dist/esm/domain/type/number-type.js +12 -0
  241. package/dist/esm/domain/type/required/required-mark.d.ts +9 -0
  242. package/dist/esm/domain/type/required/required-mark.js +9 -0
  243. package/dist/esm/domain/type/required/required.d.ts +7 -0
  244. package/dist/esm/domain/type/required/required.js +18 -0
  245. package/dist/esm/domain/type/string-type.d.ts +4 -0
  246. package/dist/esm/domain/type/string-type.js +12 -0
  247. package/dist/esm/domain/type/uuid-type.d.ts +7 -0
  248. package/dist/esm/domain/type/uuid-type.js +23 -0
  249. package/dist/esm/domain/type/value-objects/value-objects.d.ts +16 -0
  250. package/dist/esm/domain/type/value-objects/value-objects.js +66 -0
  251. package/dist/esm/exceptions/abstract.exception.d.ts +23 -0
  252. package/dist/esm/exceptions/abstract.exception.js +62 -0
  253. package/dist/esm/exceptions/application/aggregate-not-found.exception.d.ts +5 -0
  254. package/dist/esm/exceptions/application/aggregate-not-found.exception.js +13 -0
  255. package/dist/esm/exceptions/application/application.exception.d.ts +6 -0
  256. package/dist/esm/exceptions/application/application.exception.js +13 -0
  257. package/dist/esm/exceptions/application/concurrency.exception.d.ts +5 -0
  258. package/dist/esm/exceptions/application/concurrency.exception.js +13 -0
  259. package/dist/esm/exceptions/application/not-allowed.exception.d.ts +5 -0
  260. package/dist/esm/exceptions/application/not-allowed.exception.js +13 -0
  261. package/dist/esm/exceptions/brand/exception-brand.d.ts +6 -0
  262. package/dist/esm/exceptions/brand/exception-brand.js +25 -0
  263. package/dist/esm/exceptions/domain/domain.exception.d.ts +6 -0
  264. package/dist/esm/exceptions/domain/domain.exception.js +13 -0
  265. package/dist/esm/exceptions/domain/required-value.exception.d.ts +4 -0
  266. package/dist/esm/exceptions/domain/required-value.exception.js +17 -0
  267. package/dist/esm/exceptions/domain/type-primitive.exception.d.ts +4 -0
  268. package/dist/esm/exceptions/domain/type-primitive.exception.js +17 -0
  269. package/dist/esm/exceptions/domain/validation.exception.d.ts +14 -0
  270. package/dist/esm/exceptions/domain/validation.exception.js +19 -0
  271. package/dist/esm/exceptions/exception-code.d.ts +15 -0
  272. package/dist/esm/exceptions/exception-code.js +16 -0
  273. package/dist/esm/exceptions/index.d.ts +14 -0
  274. package/dist/esm/exceptions/index.js +14 -0
  275. package/dist/esm/exceptions/infrastructure/event-delivery.exception.d.ts +5 -0
  276. package/dist/esm/exceptions/infrastructure/event-delivery.exception.js +16 -0
  277. package/dist/esm/exceptions/infrastructure/infrastructure.exception.d.ts +6 -0
  278. package/dist/esm/exceptions/infrastructure/infrastructure.exception.js +13 -0
  279. package/dist/esm/exceptions/infrastructure/internal-error.exception.d.ts +5 -0
  280. package/dist/esm/exceptions/infrastructure/internal-error.exception.js +13 -0
  281. package/dist/esm/exceptions/infrastructure/unknown-event.exception.d.ts +4 -0
  282. package/dist/esm/exceptions/infrastructure/unknown-event.exception.js +15 -0
  283. package/dist/esm/exceptions/received/received-value.d.ts +4 -0
  284. package/dist/esm/exceptions/received/received-value.js +9 -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 +47 -0
  299. package/dist/esm/infrastructure/http/http-error-mapper.d.ts +23 -0
  300. package/dist/esm/infrastructure/http/http-error-mapper.js +59 -0
  301. package/dist/esm/infrastructure/index.d.ts +16 -0
  302. package/dist/esm/infrastructure/index.js +9 -0
  303. package/dist/esm/infrastructure/logging/console-logger.d.ts +17 -0
  304. package/dist/esm/infrastructure/logging/console-logger.js +33 -0
  305. package/dist/esm/infrastructure/logging/in-memory-logger.d.ts +15 -0
  306. package/dist/esm/infrastructure/logging/in-memory-logger.js +23 -0
  307. package/dist/esm/infrastructure/logging/log-serializer.d.ts +7 -0
  308. package/dist/esm/infrastructure/logging/log-serializer.js +55 -0
  309. package/dist/esm/infrastructure/logging/silent-logger.d.ts +7 -0
  310. package/dist/esm/infrastructure/logging/silent-logger.js +14 -0
  311. package/dist/esm/package.json +3 -0
  312. package/package.json +106 -4
package/SKILL.md ADDED
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: archi-code-ddd
3
+ description: The single entry point for Domain-Driven Design with @archi-code/ddd. It routes every DDD question to the guide suite shipped in the package (integration between contexts, architecture, value objects and entities, aggregates, domain events, repositories, application services, event sourcing and CQRS, audits) and gives the rules, patterns and common mistakes for TypeScript code with @archi-code/ddd - where each class goes and what it may import, value objects, Composite, Specification, Entity, AggregateRoot, EventBase, repository roles, exceptions, UseCase, the event contract (EventListener, @ListensTo, DomainEventBus, event channels), ports, persistence owned by the repository and HttpErrorMapper. Use it whenever someone applies DDD, designs or reviews a domain model, integrates contexts, writes or changes value objects, aggregates, events, use cases, listeners, repositories or adapters, decides which layer a class belongs to, or whenever the code imports @archi-code/ddd.
4
+ ---
5
+
6
+ # archi-code-ddd
7
+
8
+ `@archi-code/ddd` is archi-code's Domain-Driven Design package for TypeScript, one entry point per
9
+ layer: `@archi-code/ddd/exceptions` (the exceptions every layer throws), `@archi-code/ddd/domain`
10
+ (value objects, `Composite`, `Immutable`, `Specification`, `Entity`, `AggregateRoot`, `EventBase`
11
+ and the repository contracts `AggregateFinder`, `AggregateSaver`, `AggregateRemover`),
12
+ `@archi-code/ddd/application` (`UseCase`, the event contract to listen, `EventListener` and
13
+ `@ListensTo`, and to publish, `EventBus`, and the ports `Clock`, `Logger`) and
14
+ `@archi-code/ddd/infrastructure` (aggregate mapper for your repositories, the
15
+ event wiring `DomainEventBus`, `EventRegistry` and the `EventChannel` port, in-memory event
16
+ channel, HTTP error mapper, clocks, loggers).
17
+
18
+ ## Full API
19
+
20
+ Before using a class, function, option or type that does not appear in this skill, read
21
+ `node_modules/@archi-code/ddd/README.md`: it matches the installed version. Do not assume
22
+ signatures, options or exports. If the package is not installed: `npm install @archi-code/ddd`
23
+ (and `npm install @archi-code/validation` if validations are declared with `@Validations`).
24
+
25
+ If that path does not exist (monorepo with workspaces, pnpm…), locate the package with
26
+ `node -p "require.resolve('@archi-code/ddd/package.json')"` and read the `README.md` in that
27
+ folder.
28
+
29
+ ## The DDD guide suite
30
+
31
+ This is the **only skill to load** for Domain-Driven Design with this package. It brings a suite
32
+ of DDD guides (in Spanish, with the source of each rule: Vernon's _Implementing Domain-Driven
33
+ Design_ and _Domain-Driven Design Distilled_, and _DDD in PHP_) inside the package, in
34
+ `ddd-skills/`; never copy the suite into `.claude/skills/`: read it from the package when this
35
+ skill says so. Locate it as the README: `node_modules/@archi-code/ddd/ddd-skills/`, or the
36
+ `ddd-skills/` folder next to the `package.json` that
37
+ `node -p "require.resolve('@archi-code/ddd/package.json')"` prints (in this monorepo,
38
+ `packages/ddd/ddd-skills/`). Each guide is a folder with a `GUIA.md` (no skill frontmatter), its
39
+ `references/` and, when it has them, `assets/` (tool configuration) and `scripts/`: they are never loaded
40
+ on their own, only read when this skill routes to them. Paths inside the guides (`ddd-skills/ddd-agregados/GUIA.md`) are relative to the package
41
+ folder.
42
+
43
+ How to use it, for every design question or task:
44
+
45
+ 1. **Start at `ddd-skills/ddd-orquestador/GUIA.md`**: it says which guide covers each coding task
46
+ (integration between contexts, architecture, classes, aggregates, events, repositories, use
47
+ cases) and holds the conventions of the suite; read it for every task that is not a quick code
48
+ fix. Its `references/chuleta-reglas.md` has every essential rule with its source.
49
+ 2. **Read the `GUIA.md` of the matching guide** before answering, and the `references/` it points
50
+ to for the case at hand:
51
+
52
+ | Situation | Read `ddd-skills/…/GUIA.md` |
53
+ | ---------------------------------------------------------------------- | ------------------------------- |
54
+ | Unclear request, "where do I start", the essential rules | `ddd-orquestador` |
55
+ | How contexts and external systems relate and integrate, ACL | `ddd-context-mapping` |
56
+ | Folder structure, layers, dependencies, modules, composition root | `ddd-arquitectura` |
57
+ | Value object or entity, ids, primitives, validation, Specification | `ddd-value-objects-entidades` |
58
+ | What goes together, invariants, transactions, concurrency, factories | `ddd-agregados` |
59
+ | Events, messaging, outbox, idempotence, versions | `ddd-eventos-dominio` |
60
+ | Loading and saving aggregates, mappers, transactions, repository tests | `ddd-repositorios-persistencia` |
61
+ | Domain services, use cases, where the logic goes, authorization | `ddd-servicios-capa-aplicacion` |
62
+ | Full history, read models, CQRS, Event Sourcing | `ddd-event-sourcing-cqrs` |
63
+ | Review or migrate existing code | `ddd-auditoria-refactorizacion` |
64
+
65
+ A new project follows the orchestrator's sequence (architecture → aggregates and value objects →
66
+ events → repositories → use cases).
67
+
68
+ 3. **Design with the suite, write the code with this package.** The guides' examples are already
69
+ written with this package's API (the classes of its `src`): copy them as they are, never write
70
+ generic base classes (`ValueObject`, `Entity`, `AggregateRoot`, `DomainEvent`) of your own.
71
+ Every guide but the orchestrator opens with a "Con @archi-code/ddd" box that lists where it
72
+ diverges from the books, marked `[Fuera de los libros: convención de @archi-code/ddd]`. The
73
+ guides are the source of truth: the package departs from them only where that mark says so,
74
+ and then the package rule wins. Any other disagreement between a guide and this package
75
+ (`ddd-skills/archi-code-ddd/` or its code) is a defect of the package: follow the guide.
76
+ 4. **Audits of code built on this package** (`ddd-auditoria-refactorizacion`): apply its checklist
77
+ and also every item of `ddd-skills/archi-code-ddd/what-not-to-do.md`. Its scanner needs Python 3:
78
+ `python3 -I "$(dirname "$(node -p "require.resolve('@archi-code/ddd/package.json')")")/ddd-skills/ddd-auditoria-refactorizacion/scripts/scan_ddd_smells.py" <folder>`.
79
+ It flags a use case that saves two aggregates (`this.orders.save(…)` and `this.invoices.save(…)`
80
+ or `remove(…)`), transactions handled in the domain or the application (they belong to the
81
+ repository), domain code importing `@archi-code/ddd/application` or `/infrastructure`, and
82
+ `new Date()` in domain or application code; its findings are hints to confirm by reading the
83
+ code, and a use case that saves through a helper still needs a manual check. Without Python, apply the checklist by hand. For
84
+ the import direction, use `ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs`.
85
+ 5. Keep the suite's conventions: say which rule comes from which book and mark what does not come
86
+ from them.
87
+
88
+ If they are installed in the project, use them together with this one:
89
+
90
+ - `archi-code-validation`: available validators, their options and `@Validations` rules.
91
+
92
+ ## Rules and patterns of this package
93
+
94
+ The rules, patterns and common mistakes of `@archi-code/ddd` live next to the suite, in
95
+ `ddd-skills/archi-code-ddd/` (located like the suite). They are part of this skill: read the file
96
+ that matches the task before writing or reviewing code, and every file when unsure.
97
+
98
+ | Read `ddd-skills/archi-code-ddd/…` | When |
99
+ | ---------------------------------- | ---------------------------------------------------------------------------------------- |
100
+ | `imports.md` | Which entry point exports each class, function and type |
101
+ | `where-each-class-goes.md` | The layer of each class, what it may import, the folder layout and the names |
102
+ | `layers.md` | The rules of value objects, aggregates, events, use cases, listeners, ports and adapters |
103
+ | `patterns.md` | Code to start from: value objects, aggregates, use cases, listeners, repositories, tests |
104
+ | `what-not-to-do.md` | The common mistakes; also the checklist of every audit of code built on this package |
@@ -0,0 +1,84 @@
1
+ # Import
2
+
3
+ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
4
+
5
+ ```ts
6
+ import {
7
+ AbstractType,
8
+ AggregateRoot,
9
+ ArrayType,
10
+ BooleanType,
11
+ Composite,
12
+ CreatedAt,
13
+ DateType,
14
+ Entity,
15
+ EnumType,
16
+ EventBase,
17
+ IdType,
18
+ JsonType,
19
+ NumberType,
20
+ Required,
21
+ Specification,
22
+ StringType,
23
+ UpdatedAt,
24
+ UuidType,
25
+ ValueObjects,
26
+ } from '@archi-code/ddd/domain';
27
+ import {
28
+ AggregateNotFoundException,
29
+ ApplicationException,
30
+ ConcurrencyException,
31
+ DomainException,
32
+ ExceptionCode,
33
+ InfrastructureException,
34
+ InternalErrorException,
35
+ NotAllowedException,
36
+ RequiredValueException,
37
+ TypePrimitiveException,
38
+ UnknownEventException,
39
+ ValidationException,
40
+ } from '@archi-code/ddd/exceptions';
41
+ import type { ValidationIssue } from '@archi-code/ddd/exceptions';
42
+ import type {
43
+ AggregateFinder,
44
+ AggregateRemover,
45
+ AggregateSaver,
46
+ EntityId,
47
+ EventClass,
48
+ EventMetadata,
49
+ PrimitiveTypes,
50
+ SatisfiedBy,
51
+ SerializedEvent,
52
+ } from '@archi-code/ddd/domain';
53
+ import { EventListener, ListensTo, UseCase } from '@archi-code/ddd/application';
54
+ import type {
55
+ Clock,
56
+ EventBus,
57
+ ListenerSubscription,
58
+ LogContext,
59
+ Logger,
60
+ LogLevel,
61
+ } from '@archi-code/ddd/application';
62
+ import {
63
+ ConsoleLogger,
64
+ DomainEventBus,
65
+ EventRegistry,
66
+ FixedClock,
67
+ HttpErrorMapper,
68
+ InMemoryEventChannel,
69
+ InMemoryLogger,
70
+ SilentLogger,
71
+ SystemClock,
72
+ } from '@archi-code/ddd/infrastructure';
73
+ import type {
74
+ ChannelHandler,
75
+ ChannelSubscription,
76
+ EventChannel,
77
+ Subscription,
78
+ } from '@archi-code/ddd/infrastructure';
79
+ import { Validations } from '@archi-code/validation';
80
+ ```
81
+
82
+ ```js
83
+ const { Required, StringType } = require('@archi-code/ddd/domain');
84
+ ```
@@ -0,0 +1,299 @@
1
+ # Domain, application and infrastructure
2
+
3
+ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
4
+
5
+ - Value objects are **optional by default** (`value: T | null`) `[Fuera de los libros: convención
6
+ de @archi-code/ddd]`: the books build every value object with a value and model an optional one
7
+ as a field that may be `null` (`reservation: ReservationId | null`) [IDDD ch. 5 and 6]; both
8
+ forms work here. `Required(XType)` makes them
9
+ required: the constructor demands `T` and, if it receives an empty value (`null`, `undefined` or a
10
+ blank string), throws `RequiredValueException` with the class name. `Required(...)` also declares
11
+ the `isRequired` validation. An id is always required (see the aggregate root below).
12
+ - A domain VO is always a named subclass (`class Email extends ...`): the name appears in the error
13
+ messages, and `equals` compares the class too (`BillingCountry('ES')` is not a
14
+ `ShippingCountry('ES')`).
15
+ - **A value object that exists is valid.** The constructor converts (or throws
16
+ `TypePrimitiveException`), checks `Required` (or throws `RequiredValueException`) and runs every
17
+ `@Validations` rule, throwing one `ValidationException` with all the broken rules
18
+ (`errors: [{ field: null, code: 'isEmail', message: '<Class>: <message>' }]`; `code` is the
19
+ validator or custom rule name, or the code of the exception). There is no `assertValid()`;
20
+ `validate()` only re-runs the rules and is always ok on a built VO.
21
+ - **Report every field, never only the first.** Build the value objects of an input with
22
+ `ValueObjects.build({ field: () => new X(input.field), … })`: it runs every factory and throws one
23
+ `ValidationException` whose `errors` carry the `field` and `code` of each problem (required
24
+ `DOM004`, conversion `DOM003`, a rule name, or the code of any `DomainException`, such as your
25
+ `ORD003`); `ValueObjects.buildList(list, (item) => …)` names list items by position
26
+ (`lines.1.quantity`). It returns the command typed field by field. Factories are synchronous (one
27
+ that returns a Promise does not compile and is refused at run time with
28
+ `InternalErrorException`), and an error that is not a `DomainException` is a bug that wins over the
29
+ validation errors already collected.
30
+ - **Which layer validates what:** the input adapter checks the shape and format of the request (it
31
+ parses, a field is a string, an empty field or a length for a good user experience [IDDD ch. 4]),
32
+ never a business rule, and that never replaces the value object; value objects check each value (built in
33
+ `toCommand`); the aggregate checks invariants over several values; a domain service checks rules
34
+ over several aggregates; the use case checks authorization. The input of a use case arrives
35
+ unvalidated on purpose: every adapter goes through it.
36
+ - `@Validations` is imported from `@archi-code/validation`; a validator's parameters go in a single
37
+ `properties` object. Subclasses inherit validations, and one with the same name replaces the
38
+ parent's.
39
+ - Values are **immutable**: `_value` is `readonly`; to change a value, build a new VO.
40
+ `ArrayType.withItem/withoutItem` return a new instance; `UpdatedAt` is replaced with
41
+ `UpdatedAt.now()`.
42
+ - `toString()` is a method, `toJSON()` returns the plain value, `equals(other)` compares class and
43
+ value (dates by instant, arrays and objects by content). Template literals and `JSON.stringify`
44
+ work on VOs.
45
+ - Dates: `DateType` accepts `Date`, ISO dates (`'2024-01-31'`, UTC midnight) and ISO date-times
46
+ **with** a zone (`Z` or offset); a date-time without a zone throws `TypePrimitiveException`.
47
+ `UuidType`/`IdType` store lowercase. New ids: `OrderId.generate()` (an `OrderId`). Every id class
48
+ passes its own name as the brand (`class OrderId extends IdType<'OrderId'> {}`) so the compiler never
49
+ takes a `CustomerId` for an `OrderId`.
50
+ - Time: pass a `Clock` to `CreatedAt.now(clock)` / `UpdatedAt.now(clock)` and to events
51
+ (`new OrderPlaced(id, …, { clock })`) from use cases; tests use `FixedClock`. Aggregate methods
52
+ type it as `clock: { now(): Date }`: the `Clock` port matches it, and the domain never imports
53
+ `/application`.
54
+ - A custom type extends `AbstractType<T>` and implements only `protected filter(value: unknown): T`
55
+ (it receives input already known to be non-empty; it returns the value or throws
56
+ `TypePrimitiveException`). If restricting an existing type is enough, extend it and use
57
+ `@Validations`; to normalize the value (trim, lower case) override `filter` and call
58
+ `super.filter(value)` first: `IdType` does the same, and the rules run on the normalized value.
59
+ - **Concepts of several attributes are composite value objects**, not entities nor loose fields:
60
+ `class Money extends Composite({ amount: Cents, currency: Currency }) {}`. The schema names each
61
+ field's class: a value object class, another composite, or a list `[OrderLine]`. Fields are
62
+ exposed read-only with their value object type; the instance is frozen; `equals` compares class
63
+ and every field; `toPrimitives()` gives plain data (dates stay `Date`); `JSON.stringify` writes
64
+ the values; `X.fromPrimitives(data)` rebuilds it reporting every invalid field with its path
65
+ (`lines.1.quantity`). Rules over several fields (end ≥ start, one currency) are `@Validations`
66
+ `custom` rules on the composite class: `fn` receives the built composite, after its fields are
67
+ valid. Behavior returns new instances (`add(other): Money`). Prefer a composite over a child
68
+ entity: an order line with no id anybody looks it up by is a value [IDDD ch. 6]. A collection
69
+ with rules of its own (`OrderLines`: not empty, unique SKU, `total()`) is a composite with a list
70
+ field (`Composite({ items: [OrderLine] })`); `ArrayType` holds only simple value objects. A field
71
+ of a composite may hold an optional value object whose value is `null` (`new Line2(null)`); only
72
+ a missing instance is refused.
73
+ - The aggregate root passes the id to `super(id)`, exposed as `readonly id`; do not redeclare it
74
+ (no `_id` + getter). `equals` compares class and id only. The id is a required value object
75
+ (`EntityId`: `IdType` or a `Required(...)` type); an optional type does not compile as `Id`, and an
76
+ id that holds no value throws `RequiredValueException`, so two new entities are never equal by an
77
+ empty id [IDDD ch. 5]. A child entity (a part with an identity
78
+ of its own inside the aggregate) extends `Entity<Id>` (`super(id)`, `equals` by class and id)
79
+ and holds its id and value objects; it may have business methods named after the language
80
+ (`seat.relabel(label)`) with the rules of that part, never setters, and only its root calls them
81
+ (Tell, Don't Ask: `room.relabelSeat(id, label)` finds the seat and tells it) [IDDD ch. 5 and
82
+ 10]; rules over several parts stay in the root. Its identity is local: unique only inside its
83
+ aggregate, never used from outside [IDDD ch. 10] (`class SeatNumber extends Required(NumberType)`). `AggregateRoot` extends `Entity` too.
84
+ - **Derived values are methods, never getters** (`total()`, `isEditable()`), on aggregates, child
85
+ entities and composites: `PrimitiveTypes` sees every getter as a field, so a computed getter
86
+ breaks `toPrimitives()` at compile time.
87
+ - **A named business rule that selects, validates or combines is a `Specification<T>`**:
88
+ `class EligibleForExpressShipping extends Specification<Order> { isSatisfiedBy(order) {…} }`,
89
+ combined with `and`, `or` and `not`; `and`/`or` accept any `SatisfiedBy<T>` (an object with
90
+ `isSatisfiedBy`), the books' `Specification` interface [IDDD ch. 5]. It receives data already loaded (never a repository) and is
91
+ never used to protect an invariant of an aggregate: invariants stay inside the aggregate.
92
+ - **The domain knows nothing about persistence**: no mapper, repository nor storage concern in
93
+ domain code; a repository receives an aggregate and stores it, calling the aggregate's own
94
+ `toPrimitives()` / `fromPrimitives()`.
95
+ - **Aggregates and their primitives** (every aggregate follows these six rules so infrastructure can
96
+ store it; rules 1, 2, 4 and 5, and the name `fromPrimitives` of rule 3, are package conventions, `[Fuera de los libros: convención de
97
+ @archi-code/ddd]`: the books allow a primitive without behavior [IDDD ch. 6], getters only where
98
+ needed [Destilado ch. 5] and a mapper in infrastructure [IDDD ch. 6]):
99
+ 1. All its state is value objects: each persisted field is a value object, a composite, a list
100
+ of them or a child entity (`extends Entity<Id>`) whose fields are value objects. A flag is
101
+ `class Shipped extends Required(BooleanType) {}`, never `boolean`; a moment is `DateType` /
102
+ `CreatedAt`, never `Date`.
103
+ 2. Private field + public getter with the same name without `_` (or a public `readonly`). No
104
+ private state without its getter: the primitives use those names.
105
+ 3. Private constructor and two factories: a business one that validates and records events, and
106
+ `static fromPrimitives(primitives: PrimitiveTypes<Order>)` that rebuilds each value object
107
+ from its primitive and records nothing.
108
+ 4. `toPrimitives(): PrimitiveTypes<Order>` returns only plain data (`vo.value`, child primitives),
109
+ never a value object.
110
+ 5. Repositories only call `order.toPrimitives()` and `Order.fromPrimitives(row)` (plus
111
+ `.restoreVersion(version)`); they never read the aggregate's fields. There is no mapper.
112
+ 6. One round-trip test per aggregate: `Order.fromPrimitives(order.toPrimitives()).toPrimitives()`
113
+ equals `order.toPrimitives()`.
114
+ The compiler rejects public raw state (`PrimitiveTypes` gives `never`); nothing checks private
115
+ state at run time: a private field left out of `toPrimitives()` compiles and is lost when
116
+ saving, so rule 2 and the round-trip test of rule 6 are the aggregate's responsibility. An enum value object stores its option (`PrimitiveType` is the enum type),
117
+ so `new OrderStatus(primitives.status)` needs no cast.
118
+ - Aggregates extend `AggregateRoot<Id, Event>` and call `super(id)`. **Every aggregate has a
119
+ `version`** for optimistic concurrency: `0` when new, the stored one when loaded, `+1` after each
120
+ save. The aggregate never declares, stores nor maps it (it is not a field nor in
121
+ `PrimitiveTypes`); only repositories touch it.
122
+ - **Every repository checks the version** (contract): load with
123
+ `Order.fromPrimitives(row).restoreVersion(storedVersion)`; save a new aggregate (`version === 0`) as version 1
124
+ and treat an existing id as a conflict; save an existing one only where the stored version still
125
+ equals `aggregate.version`, storing `aggregate.version + 1` next to (never inside) the
126
+ primitives; when nothing was written throw
127
+ `ConcurrencyException('Order', id, aggregate.version)` (HTTP 409); after a write
128
+ call `aggregate.markPersisted()`. Keep the driver error: `ConcurrencyException(…, { cause })`.
129
+ Removing is a change too: `remove(aggregate)` receives the loaded aggregate and deletes only
130
+ where the stored version still equals `aggregate.version`, or throws `ConcurrencyException`
131
+ (someone changed or removed it after it was loaded) [IDDD ch. 10].
132
+ After a conflict, the use case may repeat its own work when the command is safe against the new
133
+ state [IDDD ch. 10; IDDD appendix A]: a loop in `handle` where each attempt loads the aggregate
134
+ again and saves it again (a few attempts, then rethrow); otherwise the conflict reaches the user
135
+ (409). A use case run by a listener does not retry by itself: the channel's redelivery tries
136
+ again.
137
+ - `record(event)` is `protected` and deep-freezes the event (facts never change [IDDD ch. 8]); `pullDomainEvents()` returns the events (typed `Event[]`) in order
138
+ and empties the list. Pull them after persisting, to publish. `DomainEventBus.publish` does not freeze
139
+ them again: it only sends the `toJSON()` of each event to the channel.
140
+ - Events extend `EventBase`, declare `static readonly eventName` with their class name, a fact in
141
+ the past tense (`'OrderPlaced'`) [IDDD ch. 8] (a published contract is named
142
+ `<context>.<module>.<Event>`, `sales.order.OrderPlaced` [IDDD ch. 13]) and
143
+ call `super(aggregateId, metadata)`; `event.eventName()` returns it. The payload is primitives
144
+ taken from value objects (`customer.value`, money as `{ amount, currency }`), only what
145
+ consumers need: never value objects, `toPrimitives()` nor personal data nobody needs. Listeners and registries refer to
146
+ the event by its class (`@ListensTo(OrderPlaced, { consumer: 'reserve-stock-on-order-placed' })`, `register(OrderPlaced, …)`). The
147
+ base sets `eventId` (UUID v4) and `occurredOn` (immutable `Date`, from `metadata.clock` when
148
+ given, otherwise the system time); pass `{ eventId, occurredOn }` only when rebuilding an event
149
+ a channel delivered. Inside an aggregate method, always pass the clock it received:
150
+ `new OrderPlaced(id.value, customer.value, { clock })`. Serialize with `JSON.stringify` /
151
+ `toJSON()` (`eventName`, `eventVersion`, `eventId`, `aggregateId`, ISO `occurredOn` and the
152
+ payload).
153
+ - `PrimitiveTypes<T>` only sees **public** non-method members (it strips the leading `_`): expose
154
+ VOs with public getters or public `readonly` properties. For an aggregate it includes `id`;
155
+ child entities become their primitives; raw `string`/`number`/`boolean`/`Date`
156
+ members become `never`.
157
+ - Where to throw each exception:
158
+ - Domain (aggregates, VOs, business rules): `DomainException` or a custom subclass with its own
159
+ code; `ValidationException` (thrown by value objects and `ValueObjects.build`).
160
+ - Use cases: `AggregateNotFoundException` when a repository returns `null` (except a use case
161
+ run by a listener to register a fact: it returns, so the listener is not retried forever),
162
+ `NotAllowedException` when the requester may not run the action (authorization belongs to the
163
+ use case), `ApplicationException` for other application errors.
164
+ - Adapters (DB, network…): `InfrastructureException` / `InternalErrorException`, always with
165
+ `{ cause: error }` when wrapping another error.
166
+ - `TypePrimitiveException` and `RequiredValueException` are thrown by the VOs; throw them yourself
167
+ only inside the `filter` of a custom type.
168
+ - Map errors to HTTP with `HttpErrorMapper` (overrides by code for your own codes): 400 for a
169
+ conversion or required error thrown alone (a value object built with `new` outside
170
+ `ValueObjects.build`; inside it they become a `ValidationException`, 422, with `DOM003`/`DOM004`
171
+ in `errors`), 403 `NotAllowedException`, 404 `AggregateNotFoundException`, 409
172
+ `ConcurrencyException`, 422 `ValidationException` and any other `DomainException` (a broken
173
+ business rule), 400 any other `ApplicationException`, 500 otherwise; never hand-write status tables that leak infrastructure
174
+ messages. For logs or messages use `error.code` and the exported `ExceptionCode` enum, or
175
+ `instanceof`: the built-in exceptions are recognized across the CJS and ESM builds.
176
+ - A use case does **one thing**: one business action, one class, one `handle`. It changes one
177
+ aggregate and saves it once [Destilado ch. 5; IDDD ch. 10] (the exceptions IDDD ch. 10 accepts, such
178
+ as creating several aggregates in one batch that changes nothing semantically, are written down
179
+ as an explicit decision); a flow that needs several actions is several use cases (chained by
180
+ domain events or called one after another by whoever orchestrates them), never one use case with
181
+ flags or a `type` field in its input that switch between behaviors.
182
+ - A use case is **named after the business action**, a verb and a noun from the ubiquitous language
183
+ that a domain expert would say: `PlaceOrder`, `ShipOrder`, `CancelSubscription`,
184
+ `RegisterUser`, `ApproveRefund`. Never technical or CRUD names (`CreateOrder` when the business
185
+ says "place", `UpdateOrder`, `SaveUser`, `OrderService`, `OrderManager`, `ProcessOrder`,
186
+ `HandleData`, `OrderUseCase`).
187
+ - The repository contracts belong to the domain
188
+ (`import type { AggregateSaver } from '@archi-code/ddd/domain'`). Interface segregation: a use case depends only on the repository roles it uses —
189
+ `AggregateFinder<A>` (`findById`), `AggregateSaver<A>` (`save`), `AggregateRemover<A>`
190
+ (`remove`), combined with `&` when it needs several. There is no all-in-one `Repository` port:
191
+ an adapter implements each role it supports.
192
+ - Use cases extend `UseCase<Input, Command, Output>`, receive ports by constructor
193
+ (`AggregateSaver<Order>`, `EventBus<OrderEvent>`, `Clock`) and are called
194
+ with `execute(input)`.
195
+ They implement two steps: `protected toCommand(input)` builds the value objects with
196
+ `ValueObjects.build` and returns the command (a small interface of value objects, or one value
197
+ object built with `new`); `protected handle(command)` works only with value objects. `UseCase` has no helpers:
198
+ write every step in `handle` (load with `findById` and throw `AggregateNotFoundException` when it
199
+ is `null` — or return, in a use case a listener runs —, change the aggregate, `save`, then
200
+ `publish(aggregate.pullDomainEvents())`); it never opens a transaction. Let errors
201
+ propagate: `execute` keeps package exceptions and wraps any other error (also from `toCommand`)
202
+ in `InternalErrorException` with `cause`.
203
+ - A use case never imports nor calls another use case, and **returns an id, a DTO or nothing**,
204
+ never an aggregate. Screens that read several aggregates use read queries, not repositories.
205
+ - **Authorization belongs to the use case**: the entry point takes the requester id from the token
206
+ and puts it in the input, `toCommand` builds it as a value object, and an application policy
207
+ (`SalesAccessPolicy`) throws `NotAllowedException`. A rule that is business (only the owner may
208
+ change the order) stays in the aggregate.
209
+ - **Domain services** are plain classes in `domain/` (no base class) named after the business
210
+ operation; they receive aggregates and loaded data, and may read other aggregates through an
211
+ `AggregateFinder` to validate or calculate over several of them [IDDD ch. 5 and 7]; they never
212
+ save, open transactions nor publish. A
213
+ technical one (`ExchangeRates`) is an interface in `domain/` implemented in infrastructure.
214
+ - **Repositories only get and persist** (`findById`, `save`, `remove`): no business logic, no
215
+ business queries (`pendingOlderThan`), no `nextId()`; new ids come from the value object
216
+ (`OrderId.generate()`). A `findAll()` exists only in the in-memory repository of the tests, if
217
+ they need it (`[Fuera de los libros: convención de @archi-code/ddd]`; a real repository has no
218
+ `findAll()`).
219
+ - **One repository per aggregate and technology** (user rule): it implements the roles it supports,
220
+ knows the aggregate it rebuilds, and its constructor receives only its technology (a collection,
221
+ a pool), never an aggregate class nor a mapper; aggregates only go in and out through its
222
+ methods. The aggregate converts itself (`toPrimitives()` / `static fromPrimitives()`), never a
223
+ domain service nor a mapper; the repository calls those methods directly
224
+ (`Order.fromPrimitives(row).restoreVersion(row.version)` on load, `order.toPrimitives()` with
225
+ `order.version + 1` on save, `ConcurrencyException('Order', …)` on a conflict) and maps the
226
+ primitives to its storage.
227
+ The package ships no repository: the in-memory one is written in the project's `test/support/`.
228
+ - **Another aggregate is referenced by its id** (`customerId: CustomerId`), never by the object.
229
+ - **A command can be refused, a fact cannot**: a method that registers something that already
230
+ happened elsewhere (`registerStockReservation`, called by a listener) is idempotent and never
231
+ throws a business error; if the fact does not fit, it records an event so another listener
232
+ compensates.
233
+ - **Money is a composite with whole minor units** (`Money({ amount: Cents, currency })`, `Cents` a
234
+ `Required(NumberType)` with `isInt`), never a decimal amount; it is exact up to
235
+ `Number.MAX_SAFE_INTEGER` cents (the books use `bigint`, `[Fuera de los libros: convención de
236
+ @archi-code/ddd]`); rounding is explicit.
237
+ - **The repository owns the transaction and everything about persistence** (`[Fuera de los
238
+ libros: convención de @archi-code/ddd]`; IDDD ch. 14 gives it to the application service): there
239
+ is no transaction port, and neither the domain nor the application opens, joins or closes a
240
+ transaction. Each `save`/`remove` is atomic: one document per aggregate is one write; an
241
+ aggregate over several tables is written in a database transaction the repository opens and
242
+ commits inside `save`. The repository also checks the version, manages connections and sessions
243
+ and wraps driver errors. When events must not be lost between `save` and `publish` [Destilado
244
+ ch. 6; IDDD ch. 8], the repository stores them in the same atomic write (an outbox) and a relay
245
+ delivers them through an `EventChannel`: use cases do not change.
246
+ - **Events have a schema version** (`static readonly eventVersion = 2` when their data changes
247
+ incompatibly; 1 by default, written in `toJSON()`); an `EventRegistry` factory reads older
248
+ stored versions from `json.eventVersion`.
249
+ - **The application holds only the event contract, in `application/event`**: to publish, the
250
+ port `EventBus` (`publish`, the only thing use cases see); to listen, `EventListener` and
251
+ `@ListensTo` (`ListensTo.subscriptionOf(listener)` → `{ eventName, consumer }`). What moves the
252
+ events is infrastructure, used only by the wiring (composition root), and is imported from
253
+ `@archi-code/ddd/infrastructure`: `DomainEventBus(channel, registry)` (implements `EventBus`;
254
+ `subscribe(listener)` → `Subscription { unsubscribe() }`, both async), `EventRegistry` and the
255
+ port `EventChannel` (`send(serialized)`, `receive(eventName, consumer, handler)` →
256
+ `ChannelSubscription { close() }`, with `ChannelHandler`). A messaging technology implements only
257
+ `EventChannel` (`InMemoryEventChannel` in the package; RabbitMQ, a table or Kafka in your
258
+ infrastructure); swapping it changes one line of the wiring.
259
+ - **Consumers are idempotent**: delivery is at least once, so a listener ignores an `eventId` it
260
+ already processed. Listeners always receive the event rebuilt from its JSON (frozen, equal by
261
+ `toJSON()`), also in memory, so nothing changes when the channel does.
262
+ - **Other contexts never receive domain classes**: translate the serialized event to a published,
263
+ versioned contract (`sales.order.OrderPlaced`, only what consumers need, `messageId` = `eventId`)
264
+ in the adapter that talks to the broker [IDDD ch. 13]; expose a new contract version next to the
265
+ old one while consumers move.
266
+ - Event Sourcing and CQRS are optional, only for a concrete need (full history, read models very
267
+ different from the model); read queries straight to the database come first. A projection is
268
+ an `EventListener` that writes a read table and skips an `eventId` it already applied; a read query is an application
269
+ interface implemented in infrastructure that returns DTOs or ids.
270
+ - Logging goes through the `Logger` port of `/application` (`debug`, `info`, `warn`, `error`,
271
+ each `(message, context?)`): the message is a stable snake_case name (`order_shipped`) and the
272
+ data goes in the context. Use cases, listeners and adapters that need it receive it by
273
+ constructor; the domain never logs (it records events or throws). The composition root picks the
274
+ provider: `ConsoleLogger({ level })` (one JSON line per entry), an adapter over pino or winston,
275
+ `InMemoryLogger` in tests (`entries`), `SilentLogger` by default. Pass it to
276
+ `new DomainEventBus(channel, registry, logger)` (listener failures, subscriptions, what was
277
+ published) and `new HttpErrorMapper({ statusByCode, logger })` (5xx as `error`, 4xx at `debug`):
278
+ each error is logged once, where it is handled, never logged and rethrown in every layer.
279
+ - Tests use the project's `InMemoryOrderRepository` (in `test/support/`) and
280
+ `new DomainEventBus(new InMemoryEventChannel(), registry)` (`channel.sent` lists
281
+ what was published; `{ delivery: 'parallel' }` runs the listeners of an event together).
282
+ - Listeners extend `EventListener<Event>`, are decorated with `@ListensTo(EventClass, { consumer })`
283
+ (one event per listener, any number of listeners per event; `consumer` is a stable name, unique
284
+ per listener, that the channel uses as its identity, such as its queue; never derived from the
285
+ class name, which a minifier changes), receive the one use case they run by
286
+ constructor and their `handle(event)` only maps the event to that use case's input.
287
+ - The bus is built and connected in the composition root:
288
+ `const bus = new DomainEventBus(channel, registry)`, then `await bus.subscribe(listener)` for
289
+ each listener; `subscribe` refuses a listener without `@ListensTo` and an event the registry
290
+ cannot rebuild. The use cases only receive `EventBus`.
291
+ - A real repository implements the roles it supports (`AggregateFinder<A>`, `AggregateSaver<A>`,
292
+ `AggregateRemover<A>`) and wraps driver errors in `InternalErrorException` with `cause`.
293
+ The in-memory repository of the tests (`InMemoryOrderRepository`, see `patterns.md`) stores copies
294
+ of the primitives with their version and follows the same concurrency contract.
295
+ - `DomainEventBus` rebuilds events with `EventRegistry`: register a factory per
296
+ event class that passes the JSON as metadata
297
+ (`register(OrderPlaced, (json) => new OrderPlaced(json.aggregateId, …, json))`); `fromJSON`
298
+ throws `InternalErrorException` when the rebuilt event lost its `eventId` or `occurredOn`, which
299
+ listeners need to skip a redelivery.