@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.
- package/LICENSE +46 -0
- package/README.md +2795 -2
- package/SKILL.md +106 -0
- package/ddd-skills/archi-code-ddd/imports.md +94 -0
- package/ddd-skills/archi-code-ddd/layers.md +314 -0
- package/ddd-skills/archi-code-ddd/patterns.md +683 -0
- package/ddd-skills/archi-code-ddd/what-not-to-do.md +196 -0
- package/ddd-skills/archi-code-ddd/where-each-class-goes.md +64 -0
- package/ddd-skills/ddd-agregados/GUIA.md +114 -0
- package/ddd-skills/ddd-agregados/references/consistencia-eventual.md +158 -0
- package/ddd-skills/ddd-agregados/references/ejemplo-ecommerce.md +52 -0
- package/ddd-skills/ddd-agregados/references/fabricas.md +193 -0
- package/ddd-skills/ddd-agregados/references/implementacion-ts.md +706 -0
- package/ddd-skills/ddd-arquitectura/GUIA.md +80 -0
- package/ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs +99 -0
- package/ddd-skills/ddd-arquitectura/references/estilos-adicionales.md +51 -0
- package/ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md +406 -0
- package/ddd-skills/ddd-arquitectura/references/modulos.md +48 -0
- package/ddd-skills/ddd-auditoria-refactorizacion/GUIA.md +85 -0
- package/ddd-skills/ddd-auditoria-refactorizacion/references/ejemplo-auditoria.md +314 -0
- package/ddd-skills/ddd-auditoria-refactorizacion/references/lista-comprobacion.md +54 -0
- package/ddd-skills/ddd-auditoria-refactorizacion/references/plan-migracion.md +73 -0
- package/ddd-skills/ddd-auditoria-refactorizacion/scripts/scan_ddd_smells.py +264 -0
- package/ddd-skills/ddd-context-mapping/GUIA.md +83 -0
- package/ddd-skills/ddd-context-mapping/references/anticorrupcion-ts.md +419 -0
- package/ddd-skills/ddd-context-mapping/references/estilos-integracion.md +152 -0
- package/ddd-skills/ddd-event-sourcing-cqrs/GUIA.md +92 -0
- package/ddd-skills/ddd-event-sourcing-cqrs/references/event-sourcing-ts.md +476 -0
- package/ddd-skills/ddd-event-sourcing-cqrs/references/proyecciones-cqrs-ts.md +285 -0
- package/ddd-skills/ddd-eventos-dominio/GUIA.md +106 -0
- package/ddd-skills/ddd-eventos-dominio/references/diseno-eventos.md +130 -0
- package/ddd-skills/ddd-eventos-dominio/references/ejemplo-ecommerce.md +240 -0
- package/ddd-skills/ddd-eventos-dominio/references/publicacion-ts.md +529 -0
- package/ddd-skills/ddd-orquestador/GUIA.md +40 -0
- package/ddd-skills/ddd-orquestador/references/chuleta-reglas.md +35 -0
- package/ddd-skills/ddd-repositorios-persistencia/GUIA.md +121 -0
- package/ddd-skills/ddd-repositorios-persistencia/references/implementacion-ts.md +314 -0
- package/ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md +191 -0
- package/ddd-skills/ddd-repositorios-persistencia/references/transacciones-ts.md +301 -0
- package/ddd-skills/ddd-servicios-capa-aplicacion/GUIA.md +99 -0
- package/ddd-skills/ddd-servicios-capa-aplicacion/references/capa-aplicacion-ts.md +264 -0
- package/ddd-skills/ddd-servicios-capa-aplicacion/references/ejemplo-ecommerce.md +321 -0
- package/ddd-skills/ddd-servicios-capa-aplicacion/references/servicios-dominio-ts.md +190 -0
- package/ddd-skills/ddd-value-objects-entidades/GUIA.md +118 -0
- package/ddd-skills/ddd-value-objects-entidades/references/ejemplo-ecommerce.md +87 -0
- package/ddd-skills/ddd-value-objects-entidades/references/entidades-ts.md +218 -0
- package/ddd-skills/ddd-value-objects-entidades/references/persistencia-valores.md +88 -0
- package/ddd-skills/ddd-value-objects-entidades/references/specification-y-diseno-flexible.md +119 -0
- package/ddd-skills/ddd-value-objects-entidades/references/validacion.md +96 -0
- package/ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md +376 -0
- package/dist/cjs/application/event/event-bus.d.ts +4 -0
- package/dist/cjs/application/event/event-bus.js +2 -0
- package/dist/cjs/application/event/event-listener.d.ts +4 -0
- package/dist/cjs/application/event/event-listener.js +6 -0
- package/dist/cjs/application/event/listens-to.d.ts +15 -0
- package/dist/cjs/application/event/listens-to.js +28 -0
- package/dist/cjs/application/exceptions/aggregate-not-found.exception.d.ts +5 -0
- package/dist/cjs/application/exceptions/aggregate-not-found.exception.js +17 -0
- package/dist/cjs/application/exceptions/application-exception-code.d.ts +7 -0
- package/dist/cjs/application/exceptions/application-exception-code.js +11 -0
- package/dist/cjs/application/exceptions/application.exception.d.ts +6 -0
- package/dist/cjs/application/exceptions/application.exception.js +16 -0
- package/dist/cjs/application/exceptions/concurrency.exception.d.ts +5 -0
- package/dist/cjs/application/exceptions/concurrency.exception.js +17 -0
- package/dist/cjs/application/exceptions/not-allowed.exception.d.ts +5 -0
- package/dist/cjs/application/exceptions/not-allowed.exception.js +17 -0
- package/dist/cjs/application/exceptions/unexpected-error.exception.d.ts +5 -0
- package/dist/cjs/application/exceptions/unexpected-error.exception.js +17 -0
- package/dist/cjs/application/index.d.ts +14 -0
- package/dist/cjs/application/index.js +23 -0
- package/dist/cjs/application/ports/clock.d.ts +3 -0
- package/dist/cjs/application/ports/clock.js +2 -0
- package/dist/cjs/application/ports/logger.d.ts +8 -0
- package/dist/cjs/application/ports/logger.js +2 -0
- package/dist/cjs/application/use-case/use-case.d.ts +6 -0
- package/dist/cjs/application/use-case/use-case.js +25 -0
- package/dist/cjs/application/value-object-builder/value-object-builder.d.ts +16 -0
- package/dist/cjs/application/value-object-builder/value-object-builder.js +51 -0
- package/dist/cjs/domain/aggregate/aggregate-root.d.ts +9 -0
- package/dist/cjs/domain/aggregate/aggregate-root.js +17 -0
- package/dist/cjs/domain/entity/entity.d.ts +9 -0
- package/dist/cjs/domain/entity/entity.js +19 -0
- package/dist/cjs/domain/event/event-base.d.ts +23 -0
- package/dist/cjs/domain/event/event-base.js +21 -0
- package/dist/cjs/domain/exceptions/domain-exception-code.d.ts +7 -0
- package/dist/cjs/domain/exceptions/domain-exception-code.js +11 -0
- package/dist/cjs/domain/exceptions/domain.exception.d.ts +6 -0
- package/dist/cjs/domain/exceptions/domain.exception.js +16 -0
- package/dist/cjs/domain/exceptions/invalid-event-definition.exception.d.ts +4 -0
- package/dist/cjs/domain/exceptions/invalid-event-definition.exception.js +19 -0
- package/dist/cjs/domain/exceptions/required-value.exception.d.ts +4 -0
- package/dist/cjs/domain/exceptions/required-value.exception.js +20 -0
- package/dist/cjs/domain/exceptions/type-primitive.exception.d.ts +4 -0
- package/dist/cjs/domain/exceptions/type-primitive.exception.js +20 -0
- package/dist/cjs/domain/exceptions/validation.exception.d.ts +14 -0
- package/dist/cjs/domain/exceptions/validation.exception.js +23 -0
- package/dist/cjs/domain/index.d.ts +17 -0
- package/dist/cjs/domain/index.js +36 -0
- package/dist/cjs/domain/primitive/primitive-types.d.ts +20 -0
- package/dist/cjs/domain/primitive/primitive-types.js +2 -0
- package/dist/cjs/domain/repository/aggregate-finder.d.ts +4 -0
- package/dist/cjs/domain/repository/aggregate-finder.js +2 -0
- package/dist/cjs/domain/repository/aggregate-remover.d.ts +4 -0
- package/dist/cjs/domain/repository/aggregate-remover.js +2 -0
- package/dist/cjs/domain/repository/aggregate-saver.d.ts +4 -0
- package/dist/cjs/domain/repository/aggregate-saver.js +2 -0
- package/dist/cjs/domain/specification/specification.d.ts +9 -0
- package/dist/cjs/domain/specification/specification.js +44 -0
- package/dist/cjs/domain/type/abstract-type.d.ts +18 -0
- package/dist/cjs/domain/type/abstract-type.js +61 -0
- package/dist/cjs/domain/type/array-type.d.ts +17 -0
- package/dist/cjs/domain/type/array-type.js +64 -0
- package/dist/cjs/domain/type/boolean-type.d.ts +4 -0
- package/dist/cjs/domain/type/boolean-type.js +16 -0
- package/dist/cjs/domain/type/composite/composite-brand.d.ts +4 -0
- package/dist/cjs/domain/type/composite/composite-brand.js +15 -0
- package/dist/cjs/domain/type/composite/composite-field.d.ts +18 -0
- package/dist/cjs/domain/type/composite/composite-field.js +86 -0
- package/dist/cjs/domain/type/composite/composite-issues.d.ts +6 -0
- package/dist/cjs/domain/type/composite/composite-issues.js +35 -0
- package/dist/cjs/domain/type/composite/composite.d.ts +26 -0
- package/dist/cjs/domain/type/composite/composite.js +51 -0
- package/dist/cjs/domain/type/date-type.d.ts +19 -0
- package/dist/cjs/domain/type/date-type.js +70 -0
- package/dist/cjs/domain/type/enum-type.d.ts +7 -0
- package/dist/cjs/domain/type/enum-type.js +30 -0
- package/dist/cjs/domain/type/id-type.d.ts +7 -0
- package/dist/cjs/domain/type/id-type.js +17 -0
- package/dist/cjs/domain/type/index.d.ts +15 -0
- package/dist/cjs/domain/type/index.js +29 -0
- package/dist/cjs/domain/type/json-type.d.ts +9 -0
- package/dist/cjs/domain/type/json-type.js +46 -0
- package/dist/cjs/domain/type/number-type.d.ts +4 -0
- package/dist/cjs/domain/type/number-type.js +16 -0
- package/dist/cjs/domain/type/required/required-mark.d.ts +8 -0
- package/dist/cjs/domain/type/required/required-mark.js +13 -0
- package/dist/cjs/domain/type/required/required-type.d.ts +7 -0
- package/dist/cjs/domain/type/required/required-type.js +21 -0
- package/dist/cjs/domain/type/string-type.d.ts +4 -0
- package/dist/cjs/domain/type/string-type.js +16 -0
- package/dist/cjs/domain/type/uuid-type.d.ts +7 -0
- package/dist/cjs/domain/type/uuid-type.js +27 -0
- package/dist/cjs/infrastructure/clock/fixed-clock.d.ts +8 -0
- package/dist/cjs/infrastructure/clock/fixed-clock.js +19 -0
- package/dist/cjs/infrastructure/clock/system-clock.d.ts +4 -0
- package/dist/cjs/infrastructure/clock/system-clock.js +9 -0
- package/dist/cjs/infrastructure/event/domain-event-bus.d.ts +16 -0
- package/dist/cjs/infrastructure/event/domain-event-bus.js +60 -0
- package/dist/cjs/infrastructure/event/event-channel.d.ts +9 -0
- package/dist/cjs/infrastructure/event/event-channel.js +2 -0
- package/dist/cjs/infrastructure/event/event-registry.d.ts +9 -0
- package/dist/cjs/infrastructure/event/event-registry.js +43 -0
- package/dist/cjs/infrastructure/event/in-memory/emitter.d.ts +11 -0
- package/dist/cjs/infrastructure/event/in-memory/emitter.js +54 -0
- package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.d.ts +16 -0
- package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.js +50 -0
- package/dist/cjs/infrastructure/exceptions/event-delivery.exception.d.ts +5 -0
- package/dist/cjs/infrastructure/exceptions/event-delivery.exception.js +20 -0
- package/dist/cjs/infrastructure/exceptions/infrastructure-exception-code.d.ts +6 -0
- package/dist/cjs/infrastructure/exceptions/infrastructure-exception-code.js +10 -0
- package/dist/cjs/infrastructure/exceptions/infrastructure.exception.d.ts +6 -0
- package/dist/cjs/infrastructure/exceptions/infrastructure.exception.js +16 -0
- package/dist/cjs/infrastructure/exceptions/internal-error.exception.d.ts +5 -0
- package/dist/cjs/infrastructure/exceptions/internal-error.exception.js +17 -0
- package/dist/cjs/infrastructure/exceptions/kind/exception-classifier.d.ts +4 -0
- package/dist/cjs/infrastructure/exceptions/kind/exception-classifier.js +34 -0
- package/dist/cjs/infrastructure/exceptions/kind/exception-kind.d.ts +9 -0
- package/dist/cjs/infrastructure/exceptions/kind/exception-kind.js +13 -0
- package/dist/cjs/infrastructure/exceptions/unknown-event.exception.d.ts +4 -0
- package/dist/cjs/infrastructure/exceptions/unknown-event.exception.js +19 -0
- package/dist/cjs/infrastructure/index.d.ts +21 -0
- package/dist/cjs/infrastructure/index.js +33 -0
- package/dist/cjs/infrastructure/logging/console-logger.d.ts +17 -0
- package/dist/cjs/infrastructure/logging/console-logger.js +37 -0
- package/dist/cjs/infrastructure/logging/in-memory-logger.d.ts +15 -0
- package/dist/cjs/infrastructure/logging/in-memory-logger.js +27 -0
- package/dist/cjs/infrastructure/logging/log-serializer.d.ts +7 -0
- package/dist/cjs/infrastructure/logging/log-serializer.js +59 -0
- package/dist/cjs/infrastructure/logging/silent-logger.d.ts +7 -0
- package/dist/cjs/infrastructure/logging/silent-logger.js +18 -0
- package/dist/cjs/shared/exceptions/abstract.exception.d.ts +25 -0
- package/dist/cjs/shared/exceptions/abstract.exception.js +59 -0
- package/dist/cjs/shared/exceptions/brand/exception-brand.d.ts +6 -0
- package/dist/cjs/shared/exceptions/brand/exception-brand.js +29 -0
- package/dist/cjs/shared/exceptions/received/received-value.d.ts +4 -0
- package/dist/cjs/shared/exceptions/received/received-value.js +13 -0
- package/dist/cjs/shared/index.d.ts +8 -0
- package/dist/cjs/shared/index.js +13 -0
- package/dist/cjs/shared/issue-collection-error.d.ts +9 -0
- package/dist/cjs/shared/issue-collection-error.js +11 -0
- package/dist/cjs/shared/issue-collector.d.ts +8 -0
- package/dist/cjs/shared/issue-collector.js +41 -0
- package/dist/esm/application/event/event-bus.d.ts +4 -0
- package/dist/esm/application/event/event-bus.js +1 -0
- package/dist/esm/application/event/event-listener.d.ts +4 -0
- package/dist/esm/application/event/event-listener.js +2 -0
- package/dist/esm/application/event/listens-to.d.ts +15 -0
- package/dist/esm/application/event/listens-to.js +25 -0
- package/dist/esm/application/exceptions/aggregate-not-found.exception.d.ts +5 -0
- package/dist/esm/application/exceptions/aggregate-not-found.exception.js +13 -0
- package/dist/esm/application/exceptions/application-exception-code.d.ts +7 -0
- package/dist/esm/application/exceptions/application-exception-code.js +8 -0
- package/dist/esm/application/exceptions/application.exception.d.ts +6 -0
- package/dist/esm/application/exceptions/application.exception.js +12 -0
- package/dist/esm/application/exceptions/concurrency.exception.d.ts +5 -0
- package/dist/esm/application/exceptions/concurrency.exception.js +13 -0
- package/dist/esm/application/exceptions/not-allowed.exception.d.ts +5 -0
- package/dist/esm/application/exceptions/not-allowed.exception.js +13 -0
- package/dist/esm/application/exceptions/unexpected-error.exception.d.ts +5 -0
- package/dist/esm/application/exceptions/unexpected-error.exception.js +13 -0
- package/dist/esm/application/index.d.ts +14 -0
- package/dist/esm/application/index.js +10 -0
- package/dist/esm/application/ports/clock.d.ts +3 -0
- package/dist/esm/application/ports/clock.js +1 -0
- package/dist/esm/application/ports/logger.d.ts +8 -0
- package/dist/esm/application/ports/logger.js +1 -0
- package/dist/esm/application/use-case/use-case.d.ts +6 -0
- package/dist/esm/application/use-case/use-case.js +21 -0
- package/dist/esm/application/value-object-builder/value-object-builder.d.ts +16 -0
- package/dist/esm/application/value-object-builder/value-object-builder.js +47 -0
- package/dist/esm/domain/aggregate/aggregate-root.d.ts +9 -0
- package/dist/esm/domain/aggregate/aggregate-root.js +13 -0
- package/dist/esm/domain/entity/entity.d.ts +9 -0
- package/dist/esm/domain/entity/entity.js +15 -0
- package/dist/esm/domain/event/event-base.d.ts +23 -0
- package/dist/esm/domain/event/event-base.js +17 -0
- package/dist/esm/domain/exceptions/domain-exception-code.d.ts +7 -0
- package/dist/esm/domain/exceptions/domain-exception-code.js +8 -0
- package/dist/esm/domain/exceptions/domain.exception.d.ts +6 -0
- package/dist/esm/domain/exceptions/domain.exception.js +12 -0
- package/dist/esm/domain/exceptions/invalid-event-definition.exception.d.ts +4 -0
- package/dist/esm/domain/exceptions/invalid-event-definition.exception.js +15 -0
- package/dist/esm/domain/exceptions/required-value.exception.d.ts +4 -0
- package/dist/esm/domain/exceptions/required-value.exception.js +16 -0
- package/dist/esm/domain/exceptions/type-primitive.exception.d.ts +4 -0
- package/dist/esm/domain/exceptions/type-primitive.exception.js +16 -0
- package/dist/esm/domain/exceptions/validation.exception.d.ts +14 -0
- package/dist/esm/domain/exceptions/validation.exception.js +19 -0
- package/dist/esm/domain/index.d.ts +17 -0
- package/dist/esm/domain/index.js +12 -0
- package/dist/esm/domain/primitive/primitive-types.d.ts +20 -0
- package/dist/esm/domain/primitive/primitive-types.js +1 -0
- package/dist/esm/domain/repository/aggregate-finder.d.ts +4 -0
- package/dist/esm/domain/repository/aggregate-finder.js +1 -0
- package/dist/esm/domain/repository/aggregate-remover.d.ts +4 -0
- package/dist/esm/domain/repository/aggregate-remover.js +1 -0
- package/dist/esm/domain/repository/aggregate-saver.d.ts +4 -0
- package/dist/esm/domain/repository/aggregate-saver.js +1 -0
- package/dist/esm/domain/specification/specification.d.ts +9 -0
- package/dist/esm/domain/specification/specification.js +40 -0
- package/dist/esm/domain/type/abstract-type.d.ts +18 -0
- package/dist/esm/domain/type/abstract-type.js +57 -0
- package/dist/esm/domain/type/array-type.d.ts +17 -0
- package/dist/esm/domain/type/array-type.js +60 -0
- package/dist/esm/domain/type/boolean-type.d.ts +4 -0
- package/dist/esm/domain/type/boolean-type.js +12 -0
- package/dist/esm/domain/type/composite/composite-brand.d.ts +4 -0
- package/dist/esm/domain/type/composite/composite-brand.js +11 -0
- package/dist/esm/domain/type/composite/composite-field.d.ts +18 -0
- package/dist/esm/domain/type/composite/composite-field.js +82 -0
- package/dist/esm/domain/type/composite/composite-issues.d.ts +6 -0
- package/dist/esm/domain/type/composite/composite-issues.js +31 -0
- package/dist/esm/domain/type/composite/composite.d.ts +26 -0
- package/dist/esm/domain/type/composite/composite.js +48 -0
- package/dist/esm/domain/type/date-type.d.ts +19 -0
- package/dist/esm/domain/type/date-type.js +64 -0
- package/dist/esm/domain/type/enum-type.d.ts +7 -0
- package/dist/esm/domain/type/enum-type.js +26 -0
- package/dist/esm/domain/type/id-type.d.ts +7 -0
- package/dist/esm/domain/type/id-type.js +13 -0
- package/dist/esm/domain/type/index.d.ts +15 -0
- package/dist/esm/domain/type/index.js +12 -0
- package/dist/esm/domain/type/json-type.d.ts +9 -0
- package/dist/esm/domain/type/json-type.js +42 -0
- package/dist/esm/domain/type/number-type.d.ts +4 -0
- package/dist/esm/domain/type/number-type.js +12 -0
- package/dist/esm/domain/type/required/required-mark.d.ts +8 -0
- package/dist/esm/domain/type/required/required-mark.js +9 -0
- package/dist/esm/domain/type/required/required-type.d.ts +7 -0
- package/dist/esm/domain/type/required/required-type.js +18 -0
- package/dist/esm/domain/type/string-type.d.ts +4 -0
- package/dist/esm/domain/type/string-type.js +12 -0
- package/dist/esm/domain/type/uuid-type.d.ts +7 -0
- package/dist/esm/domain/type/uuid-type.js +23 -0
- package/dist/esm/infrastructure/clock/fixed-clock.d.ts +8 -0
- package/dist/esm/infrastructure/clock/fixed-clock.js +15 -0
- package/dist/esm/infrastructure/clock/system-clock.d.ts +4 -0
- package/dist/esm/infrastructure/clock/system-clock.js +5 -0
- package/dist/esm/infrastructure/event/domain-event-bus.d.ts +16 -0
- package/dist/esm/infrastructure/event/domain-event-bus.js +56 -0
- package/dist/esm/infrastructure/event/event-channel.d.ts +9 -0
- package/dist/esm/infrastructure/event/event-channel.js +1 -0
- package/dist/esm/infrastructure/event/event-registry.d.ts +9 -0
- package/dist/esm/infrastructure/event/event-registry.js +39 -0
- package/dist/esm/infrastructure/event/in-memory/emitter.d.ts +11 -0
- package/dist/esm/infrastructure/event/in-memory/emitter.js +50 -0
- package/dist/esm/infrastructure/event/in-memory/in-memory-event-channel.d.ts +16 -0
- package/dist/esm/infrastructure/event/in-memory/in-memory-event-channel.js +46 -0
- package/dist/esm/infrastructure/exceptions/event-delivery.exception.d.ts +5 -0
- package/dist/esm/infrastructure/exceptions/event-delivery.exception.js +16 -0
- package/dist/esm/infrastructure/exceptions/infrastructure-exception-code.d.ts +6 -0
- package/dist/esm/infrastructure/exceptions/infrastructure-exception-code.js +7 -0
- package/dist/esm/infrastructure/exceptions/infrastructure.exception.d.ts +6 -0
- package/dist/esm/infrastructure/exceptions/infrastructure.exception.js +12 -0
- package/dist/esm/infrastructure/exceptions/internal-error.exception.d.ts +5 -0
- package/dist/esm/infrastructure/exceptions/internal-error.exception.js +13 -0
- package/dist/esm/infrastructure/exceptions/kind/exception-classifier.d.ts +4 -0
- package/dist/esm/infrastructure/exceptions/kind/exception-classifier.js +30 -0
- package/dist/esm/infrastructure/exceptions/kind/exception-kind.d.ts +9 -0
- package/dist/esm/infrastructure/exceptions/kind/exception-kind.js +10 -0
- package/dist/esm/infrastructure/exceptions/unknown-event.exception.d.ts +4 -0
- package/dist/esm/infrastructure/exceptions/unknown-event.exception.js +15 -0
- package/dist/esm/infrastructure/index.d.ts +21 -0
- package/dist/esm/infrastructure/index.js +15 -0
- package/dist/esm/infrastructure/logging/console-logger.d.ts +17 -0
- package/dist/esm/infrastructure/logging/console-logger.js +33 -0
- package/dist/esm/infrastructure/logging/in-memory-logger.d.ts +15 -0
- package/dist/esm/infrastructure/logging/in-memory-logger.js +23 -0
- package/dist/esm/infrastructure/logging/log-serializer.d.ts +7 -0
- package/dist/esm/infrastructure/logging/log-serializer.js +55 -0
- package/dist/esm/infrastructure/logging/silent-logger.d.ts +7 -0
- package/dist/esm/infrastructure/logging/silent-logger.js +14 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/shared/exceptions/abstract.exception.d.ts +25 -0
- package/dist/esm/shared/exceptions/abstract.exception.js +55 -0
- package/dist/esm/shared/exceptions/brand/exception-brand.d.ts +6 -0
- package/dist/esm/shared/exceptions/brand/exception-brand.js +25 -0
- package/dist/esm/shared/exceptions/received/received-value.d.ts +4 -0
- package/dist/esm/shared/exceptions/received/received-value.js +9 -0
- package/dist/esm/shared/index.d.ts +8 -0
- package/dist/esm/shared/index.js +5 -0
- package/dist/esm/shared/issue-collection-error.d.ts +9 -0
- package/dist/esm/shared/issue-collection-error.js +7 -0
- package/dist/esm/shared/issue-collector.d.ts +8 -0
- package/dist/esm/shared/issue-collector.js +37 -0
- package/package.json +95 -4
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Escaneo heurístico de síntomas DDD en un repositorio TypeScript.
|
|
4
|
+
|
|
5
|
+
Uso:
|
|
6
|
+
python3 scan_ddd_smells.py <ruta> [--json salida.json] [--domain-dir domain]
|
|
7
|
+
|
|
8
|
+
Solo lee ficheros. Los hallazgos son indicios que hay que confirmar leyendo el código.
|
|
9
|
+
"""
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
from collections import defaultdict
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
INFRA_PACKAGES = (
|
|
18
|
+
"express", "fastify", "koa", "@nestjs/", "@prisma/client", "prisma", "typeorm", "sequelize",
|
|
19
|
+
"mongoose", "mongodb", "pg", "mysql", "mysql2", "knex", "drizzle-orm", "@mikro-orm/", "axios",
|
|
20
|
+
"node-fetch", "kafkajs", "amqplib", "@azure/", "aws-sdk", "@aws-sdk/", "redis", "ioredis",
|
|
21
|
+
)
|
|
22
|
+
IGNORED_DIRS = {"node_modules", "dist", "build", ".git", "coverage", ".next", ".tmp"}
|
|
23
|
+
|
|
24
|
+
IMPORT_RE = re.compile(r"""^\s*import\s+(?:[^'"]+\s+from\s+)?['"]([^'"]+)['"]""", re.M)
|
|
25
|
+
REQUIRE_RE = re.compile(r"""require\(\s*['"]([^'"]+)['"]\s*\)""")
|
|
26
|
+
SETTER_RE = re.compile(r"^\s*(?:public\s+)?set\s*([A-Z]\w*)\s*\(|^\s*(?:public\s+)?set\s+\w+\s*\(", re.M)
|
|
27
|
+
PUBLIC_MUTABLE_FIELD_RE = re.compile(r"^\s*public\s+(?!readonly)(?!static)\w+\??\s*:", re.M)
|
|
28
|
+
CLASS_RE = re.compile(r"\bclass\s+(\w+)")
|
|
29
|
+
ORM_DECORATOR_RE = re.compile(r"@(Entity|Column|PrimaryGeneratedColumn|OneToMany|ManyToOne|Table|Prop|Schema)\b")
|
|
30
|
+
PRIMITIVE_MONEY_RE = re.compile(r"\b_?(price|amount|total|importe|precio)\s*\??\s*:\s*number\b", re.I)
|
|
31
|
+
PRIMITIVE_STATUS_RE = re.compile(r"\b_?(status|estado|state)\s*\??\s*:\s*string\b", re.I)
|
|
32
|
+
SMELLY_NAME_RE = re.compile(r"\bclass\s+\w*(Manager|Helper|Utils?|Processor)\b")
|
|
33
|
+
REPO_CLASS_RE = re.compile(r"\b(?:class|interface)\s+(\w+)Repository\b")
|
|
34
|
+
# Cualquier receptor que guarde: this.orders.save(...), orderRepository.save(...), repo.persist(...)
|
|
35
|
+
SAVE_CALL_RE = re.compile(r"(?:this\.)?(\w+)\s*\.\s*(?:save|add|persist|remove)\s*\(")
|
|
36
|
+
NOT_A_REPOSITORY = {"events", "eventbus", "bus", "outbox", "inbox", "list", "set", "map", "array", "items", "tags"}
|
|
37
|
+
ARCHI_OUTER_LAYER_RE = re.compile(r"""['"]@archi-code/ddd/(application|infrastructure)['"]""")
|
|
38
|
+
NEW_DATE_RE = re.compile(r"\bnew\s+Date\s*\(\s*\)")
|
|
39
|
+
TRANSACTION_START_RE = re.compile(r"(\$transaction|\.transaction|\btx\.run|\.run|runInTransaction|withTransaction)\s*\(")
|
|
40
|
+
# La transacción es del adaptador del repositorio: dominio y aplicación no abren, reciben ni manejan transacciones
|
|
41
|
+
TRANSACTION_HANDLING_RE = re.compile(
|
|
42
|
+
r"\b(TransactionRunner|TransactionManager|UnitOfWork|unitOfWork|startSession|startTransaction|commitTransaction|"
|
|
43
|
+
r"abortTransaction|withTransaction|runInTransaction|inTransaction)\b|\$transaction\b|['\"`]\s*(BEGIN|COMMIT|ROLLBACK)\b"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def iter_ts_files(root: Path):
|
|
48
|
+
for p in root.rglob("*"):
|
|
49
|
+
if p.is_dir() or any(part in IGNORED_DIRS for part in p.parts):
|
|
50
|
+
continue
|
|
51
|
+
if p.suffix in (".ts", ".tsx", ".mts", ".cts") and not p.name.endswith(".d.ts"):
|
|
52
|
+
yield p
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def zone_of(path: Path, domain_dir: str):
|
|
56
|
+
parts = [p.lower() for p in path.parts]
|
|
57
|
+
for zone in (domain_dir, "application", "infrastructure", "interfaces"):
|
|
58
|
+
if zone in parts:
|
|
59
|
+
return zone
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def context_of(path: Path):
|
|
64
|
+
parts = list(path.parts)
|
|
65
|
+
for anchor in ("contexts", "bounded-contexts", "modules"):
|
|
66
|
+
if anchor in parts:
|
|
67
|
+
i = parts.index(anchor)
|
|
68
|
+
if i + 1 < len(parts):
|
|
69
|
+
return parts[i + 1]
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def transaction_blocks(text: str):
|
|
74
|
+
"""Devuelve (posición, contenido) de cada llamada transaccional, emparejando paréntesis."""
|
|
75
|
+
for m in TRANSACTION_START_RE.finditer(text):
|
|
76
|
+
if m.group(1) == ".run" and not re.search(r"(tx|transaction|uow|unitOfWork)\w*\s*$", text[:m.start()], re.I):
|
|
77
|
+
continue # .run( de otra cosa
|
|
78
|
+
i = m.end() - 1 # posición del '('
|
|
79
|
+
depth, j = 0, i
|
|
80
|
+
while j < len(text):
|
|
81
|
+
c = text[j]
|
|
82
|
+
if c == "(":
|
|
83
|
+
depth += 1
|
|
84
|
+
elif c == ")":
|
|
85
|
+
depth -= 1
|
|
86
|
+
if depth == 0:
|
|
87
|
+
break
|
|
88
|
+
j += 1
|
|
89
|
+
yield m.start(), text[i:j + 1]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def class_spans(text: str):
|
|
93
|
+
"""Devuelve (inicio, fin, nombre) de cada clase: desde su declaración hasta la siguiente o el final."""
|
|
94
|
+
starts = [(m.start(), m.group(1)) for m in CLASS_RE.finditer(text)]
|
|
95
|
+
for k, (start, name) in enumerate(starts):
|
|
96
|
+
end = starts[k + 1][0] if k + 1 < len(starts) else len(text)
|
|
97
|
+
yield start, end, name
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def line_of(text: str, idx: int) -> int:
|
|
101
|
+
return text.count("\n", 0, idx) + 1
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def scan(root: Path, domain_dir: str):
|
|
105
|
+
findings = []
|
|
106
|
+
stats = defaultdict(int)
|
|
107
|
+
repos = []
|
|
108
|
+
|
|
109
|
+
def add(rule, severity, path, line, detail):
|
|
110
|
+
findings.append({"rule": rule, "severity": severity, "file": str(path.relative_to(root)), "line": line, "detail": detail})
|
|
111
|
+
|
|
112
|
+
for path in iter_ts_files(root):
|
|
113
|
+
try:
|
|
114
|
+
text = path.read_text(encoding="utf-8", errors="ignore")
|
|
115
|
+
except OSError:
|
|
116
|
+
continue
|
|
117
|
+
stats["ficheros_ts"] += 1
|
|
118
|
+
zone = zone_of(path, domain_dir)
|
|
119
|
+
ctx = context_of(path)
|
|
120
|
+
is_test = ".test." in path.name or ".spec." in path.name
|
|
121
|
+
|
|
122
|
+
imports = [(m.group(1), m.start()) for m in IMPORT_RE.finditer(text)] + \
|
|
123
|
+
[(m.group(1), m.start()) for m in REQUIRE_RE.finditer(text)]
|
|
124
|
+
|
|
125
|
+
if zone == domain_dir and not is_test:
|
|
126
|
+
stats["ficheros_dominio"] += 1
|
|
127
|
+
for mod, idx in imports:
|
|
128
|
+
if any(mod == p or mod.startswith(p if p.endswith("/") else p + "/") or mod == p.rstrip("/") for p in INFRA_PACKAGES):
|
|
129
|
+
add("dominio-importa-infraestructura", "alta", path, line_of(text, idx), f"importa '{mod}'")
|
|
130
|
+
if not mod.startswith("@archi-code/") and re.search(r"/(infrastructure|interfaces|application)(/|$)", mod):
|
|
131
|
+
add("dominio-importa-capa-exterior", "alta", path, line_of(text, idx), f"importa '{mod}'")
|
|
132
|
+
for m in ARCHI_OUTER_LAYER_RE.finditer(text):
|
|
133
|
+
add("dominio-importa-capa-exterior-del-paquete", "alta", path, line_of(text, m.start()),
|
|
134
|
+
f"importa @archi-code/ddd/{m.group(1)}")
|
|
135
|
+
if "process.env" in text:
|
|
136
|
+
add("dominio-lee-entorno", "media", path, line_of(text, text.index("process.env")), "usa process.env")
|
|
137
|
+
for m in ORM_DECORATOR_RE.finditer(text):
|
|
138
|
+
add("decorador-orm-en-dominio", "alta", path, line_of(text, m.start()), f"@{m.group(1)}")
|
|
139
|
+
for m in SETTER_RE.finditer(text):
|
|
140
|
+
add("setter-en-dominio", "media", path, line_of(text, m.start()), m.group(0).strip()[:60])
|
|
141
|
+
for m in PUBLIC_MUTABLE_FIELD_RE.finditer(text):
|
|
142
|
+
add("campo-publico-mutable", "media", path, line_of(text, m.start()), m.group(0).strip()[:60])
|
|
143
|
+
for m in PRIMITIVE_MONEY_RE.finditer(text):
|
|
144
|
+
add("dinero-como-number", "media", path, line_of(text, m.start()), m.group(0))
|
|
145
|
+
for m in PRIMITIVE_STATUS_RE.finditer(text):
|
|
146
|
+
add("estado-como-string", "baja", path, line_of(text, m.start()), m.group(0))
|
|
147
|
+
|
|
148
|
+
if zone in (domain_dir, "application") and not is_test:
|
|
149
|
+
for m in NEW_DATE_RE.finditer(text):
|
|
150
|
+
add("new-date-en-dominio-o-aplicacion", "media", path, line_of(text, m.start()),
|
|
151
|
+
"usa new Date(); inyecta un Clock")
|
|
152
|
+
|
|
153
|
+
if zone == "interfaces" and not is_test:
|
|
154
|
+
if re.search(r"Repository\b", text):
|
|
155
|
+
for m in re.finditer(r"\w+Repository\b", text):
|
|
156
|
+
add("interfaz-usa-repositorio", "media", path, line_of(text, m.start()), m.group(0))
|
|
157
|
+
break
|
|
158
|
+
for m in re.finditer(r"\bprisma\.\w+\.(create|update|delete|upsert)", text):
|
|
159
|
+
add("controlador-escribe-en-bd", "alta", path, line_of(text, m.start()), m.group(0))
|
|
160
|
+
|
|
161
|
+
if ctx and not is_test:
|
|
162
|
+
for mod, idx in imports:
|
|
163
|
+
target_ctx = None
|
|
164
|
+
if mod.startswith("."):
|
|
165
|
+
target_ctx = context_of((path.parent / mod).resolve())
|
|
166
|
+
else:
|
|
167
|
+
m = re.search(r"contexts/([\w-]+)/", mod)
|
|
168
|
+
target_ctx = m.group(1) if m else None
|
|
169
|
+
if target_ctx and target_ctx != ctx:
|
|
170
|
+
add("importacion-entre-contextos", "alta", path, line_of(text, idx), f"{ctx} importa de {target_ctx}")
|
|
171
|
+
|
|
172
|
+
for m in SMELLY_NAME_RE.finditer(text):
|
|
173
|
+
add("clase-con-nombre-generico", "baja", path, line_of(text, m.start()), m.group(0))
|
|
174
|
+
|
|
175
|
+
for m in REPO_CLASS_RE.finditer(text):
|
|
176
|
+
repos.append((m.group(1), str(path.relative_to(root))))
|
|
177
|
+
|
|
178
|
+
multi_save_blocks = []
|
|
179
|
+
if not is_test:
|
|
180
|
+
for start, block in transaction_blocks(text):
|
|
181
|
+
saved = {m.group(1).lower() for m in SAVE_CALL_RE.finditer(block)} - NOT_A_REPOSITORY
|
|
182
|
+
if len(saved) >= 2:
|
|
183
|
+
multi_save_blocks.append(start)
|
|
184
|
+
add("varios-agregados-en-una-transaccion", "alta", path, line_of(text, start),
|
|
185
|
+
f"el mismo bloque transaccional guarda con {sorted(saved)}")
|
|
186
|
+
|
|
187
|
+
if zone in (domain_dir, "application") and not is_test:
|
|
188
|
+
m = TRANSACTION_HANDLING_RE.search(text)
|
|
189
|
+
if m is None:
|
|
190
|
+
m = next((TRANSACTION_START_RE.search(text, s) for s, _ in transaction_blocks(text)), None)
|
|
191
|
+
if m is not None:
|
|
192
|
+
rule = "transaccion-en-dominio" if zone == domain_dir else "transaccion-en-aplicacion"
|
|
193
|
+
severity = "alta" if zone == domain_dir else "media"
|
|
194
|
+
add(rule, severity, path, line_of(text, m.start()),
|
|
195
|
+
f"maneja transacciones ({m.group(0).strip()[:40]}); muévelo al adaptador del repositorio")
|
|
196
|
+
|
|
197
|
+
if zone == "application" and not is_test:
|
|
198
|
+
for start, end, name in class_spans(text):
|
|
199
|
+
if any(start <= b < end for b in multi_save_blocks):
|
|
200
|
+
continue
|
|
201
|
+
saved = {m.group(1).lower() for m in SAVE_CALL_RE.finditer(text, start, end)} - NOT_A_REPOSITORY
|
|
202
|
+
if len(saved) >= 2:
|
|
203
|
+
add("caso-de-uso-guarda-varios-agregados", "alta", path, line_of(text, start),
|
|
204
|
+
f"{name} guarda con {sorted(saved)}")
|
|
205
|
+
|
|
206
|
+
if not is_test and zone in ("application", None):
|
|
207
|
+
for cm in CLASS_RE.finditer(text):
|
|
208
|
+
if cm.group(1).endswith("Service"):
|
|
209
|
+
methods = len(re.findall(r"^\s+(?:public\s+|async\s+)+\w+\s*\(", text, re.M))
|
|
210
|
+
loc = text.count("\n")
|
|
211
|
+
if methods > 15 or loc > 400:
|
|
212
|
+
add("servicio-grande", "media", path, line_of(text, cm.start()),
|
|
213
|
+
f"{cm.group(1)}: ~{methods} métodos, {loc} líneas")
|
|
214
|
+
|
|
215
|
+
part_names = ("Line", "Item", "Detail", "Address", "Linea", "Detalle", "Direccion")
|
|
216
|
+
for name, f in sorted(set(repos)):
|
|
217
|
+
if name.endswith(part_names):
|
|
218
|
+
findings.append({"rule": "repositorio-de-parte-interna", "severity": "media", "file": f, "line": 1,
|
|
219
|
+
"detail": f"{name}Repository: ¿es una parte de otro agregado?"})
|
|
220
|
+
stats["repositorios"] = len(set(r[0] for r in repos))
|
|
221
|
+
return findings, dict(stats)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def main():
|
|
225
|
+
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
226
|
+
ap.add_argument("root")
|
|
227
|
+
ap.add_argument("--json", dest="json_out")
|
|
228
|
+
ap.add_argument("--domain-dir", default="domain")
|
|
229
|
+
args = ap.parse_args()
|
|
230
|
+
|
|
231
|
+
root = Path(args.root).resolve()
|
|
232
|
+
if not root.is_dir():
|
|
233
|
+
print(f"No existe el directorio: {root}", file=sys.stderr)
|
|
234
|
+
sys.exit(2)
|
|
235
|
+
|
|
236
|
+
findings, stats = scan(root, args.domain_dir.lower())
|
|
237
|
+
order = {"critica": 0, "alta": 1, "media": 2, "baja": 3}
|
|
238
|
+
findings.sort(key=lambda f: (order.get(f["severity"], 9), f["rule"], f["file"], f["line"]))
|
|
239
|
+
|
|
240
|
+
by_rule = defaultdict(int)
|
|
241
|
+
for f in findings:
|
|
242
|
+
by_rule[(f["severity"], f["rule"])] += 1
|
|
243
|
+
|
|
244
|
+
print(f"Repositorio: {root}")
|
|
245
|
+
print("Estadísticas: " + ", ".join(f"{k}={v}" for k, v in stats.items()))
|
|
246
|
+
if stats.get("ficheros_dominio", 0) == 0:
|
|
247
|
+
print("Aviso: no se ha encontrado ninguna carpeta 'domain'. Las reglas de dominio no se han aplicado;"
|
|
248
|
+
" usa --domain-dir si se llama de otra forma, o revisa a mano.")
|
|
249
|
+
print("\nResumen por regla:")
|
|
250
|
+
for (sev, rule), n in sorted(by_rule.items(), key=lambda x: (order.get(x[0][0], 9), x[0][1])):
|
|
251
|
+
print(f" [{sev:5}] {rule}: {n}")
|
|
252
|
+
print("\nPrimeros hallazgos:")
|
|
253
|
+
for f in findings[:40]:
|
|
254
|
+
print(f" [{f['severity']:5}] {f['rule']} {f['file']}:{f['line']} {f['detail']}")
|
|
255
|
+
if len(findings) > 40:
|
|
256
|
+
print(f" ... y {len(findings) - 40} más (usa --json para la lista completa)")
|
|
257
|
+
|
|
258
|
+
if args.json_out:
|
|
259
|
+
Path(args.json_out).write_text(json.dumps({"stats": stats, "findings": findings}, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
260
|
+
print(f"\nJSON escrito en {args.json_out}")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
if __name__ == "__main__":
|
|
264
|
+
main()
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Context Mapping
|
|
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.** Context Mapping de DDD para decidir cómo se integran en el código los Bounded Contexts. Cubre los patrones de integración entre modelos (Shared Kernel, Conformist, Anticorruption Layer, Open Host Service, Published Language, Separate Ways, Big Ball of Mud), upstream y downstream, estilos de integración (RPC, REST, mensajería con eventos), contratos e idioma publicado, idempotencia, consistencia eventual entre contextos, procesos de larga duración y cómo implementar una capa anticorrupción en TypeScript. Consúltala siempre que haya que conectar dos sistemas, servicios o módulos, integrar con un ERP, CRM, pasarela o sistema heredado, decidir entre llamada síncrona y eventos, diseñar una API pública entre servicios, o cuando un modelo empiece a copiar conceptos de otro, aunque no se mencione "context map".
|
|
6
|
+
|
|
7
|
+
## Con @archi-code/ddd
|
|
8
|
+
|
|
9
|
+
[Fuera de los libros: convención de @archi-code/ddd]
|
|
10
|
+
|
|
11
|
+
* El paquete no trae capa anticorrupción, Open Host Service ni Published Language: se escriben en el proyecto sobre sus clases y puertos. El puerto hacia otro contexto (`ProductPricing`) es una interfaz en `domain/` que recibe y devuelve value objects (`Sku`, un `Composite` `SellableProduct`); adaptador (`CatalogHttpProductPricing`) y traductor (`CatalogTranslator`) van en `infrastructure/` y lanzan una `InfrastructureException` con código propio (`CAT001`), que el punto de entrada traduce por su código (`CAT001` → 503 en HTTP; sin traducción propia, `ExceptionClassifier.classify` la clasifica como `ExceptionKind.Internal`).
|
|
12
|
+
* Un webhook entrante es un punto de entrada en `interfaces/` que traduce el lenguaje del emisor a la entrada de un `UseCase` y responde según el `ExceptionKind` que devuelve `ExceptionClassifier.classify(error)` (de `@archi-code/ddd/infrastructure`).
|
|
13
|
+
* Los mensajes entre contextos viajan como `SerializedEvent` plano (`{ eventName, eventId, occurredOn, ...datos }`, con la identidad del agregado entre los datos, como `orderId`) por un `EventChannel` (tu canal de broker o de outbox), no con el envoltorio de IDDD `{ notificationId, typeName, version, occurredOn, event }`: `eventName` hace de `typeName` (`'sales.order.OrderPlaced'`) y también identifica la versión del contrato (una versión incompatible se publica con otro nombre, como `'sales.order.OrderPlacedV2'`), y `eventId` identifica el mensaje para deduplicar. No hay `notificationId` secuencial: si un feed REST lo necesita, lo asigna tu canal duradero.
|
|
14
|
+
* Lo que sale se traduce al Published Language en infraestructura, con un `EventChannel` propio en el adaptador del broker (`ContractPublishingChannel` + `SalesContracts`, en `ddd-skills/ddd-eventos-dominio/references/publicacion-ts.md`); nunca se publica fuera del contexto un evento interno tal cual (`{ ...event }`).
|
|
15
|
+
* Lo que entra de otro contexto se lee con una clase propia `extends EventBase` que lleva el nombre publicado y solo los campos que este contexto usa; la factoría de `EventRegistry` es el traductor, y un `EventListener` con `@ListensTo(Evento, { consumer })` ejecuta un caso de uso, que valida cada campo con value objects en `toCommand`.
|
|
16
|
+
* Los procesos entre contextos se encadenan con eventos y listeners, cada uno con su caso de uso.
|
|
17
|
+
|
|
18
|
+
Una línea entre dos Bounded Contexts significa **integración técnica** y **traducción entre dos lenguajes** [Destilado cap. 4]. Esta guía decide el tipo de cada línea y cómo se implementa.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Flujo de trabajo
|
|
22
|
+
|
|
23
|
+
### Paso 1. Elegir el patrón de integración para cada relación
|
|
24
|
+
|
|
25
|
+
Para cada par de contextos que se relacionan, anota quién es upstream (U, de quien depende el otro) y quién downstream (D) [Destilado cap. 4]. Resumen [Destilado cap. 4]:
|
|
26
|
+
|
|
27
|
+
| Patrón | Esencia |
|
|
28
|
+
|---|---|
|
|
29
|
+
| Shared Kernel | Pequeño modelo común compartido por acuerdo explícito. Difícil de mantener; se evita si es posible [IDDD cap. 2] |
|
|
30
|
+
| Conformist | El downstream adopta el modelo upstream tal cual |
|
|
31
|
+
| Anticorruption Layer | El downstream traduce entre el modelo upstream y el suyo; la opción más defensiva |
|
|
32
|
+
| Open Host Service | El upstream ofrece un protocolo abierto, documentado y cómodo para cualquier integrador |
|
|
33
|
+
| Published Language | Formato de intercambio documentado (JSON Schema, Protobuf, Avro); suele ir con OHS |
|
|
34
|
+
| Separate Ways | No integrar: la integración no aporta lo suficiente |
|
|
35
|
+
| Big Ball of Mud | Sistema sin fronteras; si hay que integrarse, siempre con ACL y sin hablar su lenguaje |
|
|
36
|
+
|
|
37
|
+
Árbol de decisión rápido `[Fuera de los libros: síntesis de las definiciones]`:
|
|
38
|
+
|
|
39
|
+
1. ¿La integración aporta valor real? No → Separate Ways.
|
|
40
|
+
2. ¿Eres downstream? Entonces:
|
|
41
|
+
* ¿El upstream es un Big Ball of Mud o un producto externo? → ACL, siempre.
|
|
42
|
+
* ¿Tu contexto es Core? → ACL, salvo coste desproporcionado.
|
|
43
|
+
* ¿El upstream ofrece OHS con Published Language y su modelo se parece al tuyo? → Conformist puede ser aceptable.
|
|
44
|
+
3. ¿Eres upstream con varios consumidores? → Open Host Service con Published Language.
|
|
45
|
+
4. ¿La tentación es compartir clases o tablas? → Para y revisa: casi siempre se resuelve mejor con Published Language.
|
|
46
|
+
|
|
47
|
+
### Paso 2. Elegir el estilo de integración
|
|
48
|
+
|
|
49
|
+
Vernon los ordena de menos a más robusto: RPC, REST, mensajería [Destilado cap. 4]. Detalle en `ddd-skills/ddd-context-mapping/references/estilos-integracion.md`.
|
|
50
|
+
|
|
51
|
+
Reglas que aplican siempre:
|
|
52
|
+
|
|
53
|
+
* **Integración por base de datos o ficheros:** evitarla. Si es inevitable, aislar el modelo con ACL [Destilado cap. 4].
|
|
54
|
+
* **Asume los principios de la computación distribuida:** la red falla, hay latencia, el ancho de banda no es infinito, la red no es segura, la topología cambia, hay varios administradores, el transporte cuesta y la red es heterogénea [IDDD cap. 13].
|
|
55
|
+
* **Evita los choques de trenes de integración:** si C0 hace una petición bloqueante a C1, C1 no debe hacer a su vez una petición bloqueante a S1 para responderla. Usa mensajería [Destilado cap. 4].
|
|
56
|
+
* **Recursos REST sintéticos:** diseñados para los casos de uso del cliente, no como espejo de los agregados. Exponer el modelo tal cual obliga a todos a ser Conformist [Destilado cap. 4; IDDD cap. 13].
|
|
57
|
+
* **Los consumidores dependen del esquema, no de las clases del productor.** Leen el Published Language (por ejemplo JSON) y lo traducen a sus propios tipos [Destilado cap. 4; IDDD cap. 13].
|
|
58
|
+
* **Mensajería:** el mecanismo debe garantizar entrega al menos una vez y el receptor debe ser idempotente [Destilado cap. 4].
|
|
59
|
+
* **Estado remoto mínimo:** copia solo los atributos que tu modelo necesita y modélalos con tus conceptos. Si no, tu `ProductOwner` acaba siendo un `User` disfrazado [IDDD cap. 3].
|
|
60
|
+
* **La indisponibilidad se modela:** "todavía no está" es un estado válido del dominio, no un error. IDDD lo resuelve con un estado explícito en un value object [IDDD cap. 3].
|
|
61
|
+
|
|
62
|
+
### Paso 3. Diseñar la traducción
|
|
63
|
+
|
|
64
|
+
Para cada relación con ACL, define puerto, adaptador y traductor. Implementación con `@archi-code/ddd` (puerto con value objects, adaptador HTTP, webhook entrante, eventos de otro contexto y Published Language de salida) en `ddd-skills/ddd-context-mapping/references/anticorrupcion-ts.md`.
|
|
65
|
+
|
|
66
|
+
Para cada evento que cruza un contexto, decide entre **evento enriquecido** (más autonomía del consumidor, pero difícil prever todo y riesgo de exponer datos sensibles) o **evento ligero con consulta al origen** (query-back), o una mezcla [Destilado cap. 4].
|
|
67
|
+
|
|
68
|
+
## Señales de alarma
|
|
69
|
+
|
|
70
|
+
* Un contexto importa tipos (clases, interfaces, enums) del dominio de otro.
|
|
71
|
+
* Tablas compartidas entre contextos.
|
|
72
|
+
* API REST que devuelve el agregado serializado con todos sus campos.
|
|
73
|
+
* Una petición de usuario que atraviesa tres servicios en cadena de forma síncrona.
|
|
74
|
+
* Un consumidor de eventos sin control de duplicados.
|
|
75
|
+
* Un contexto publica hacia fuera sus eventos internos tal cual (`{ ...event }`), o un listener recibe la clase de evento de otro contexto importada de su código.
|
|
76
|
+
* "Shared Kernel" usado como excusa para compartir una librería de modelos que crece sin acuerdo [IDDD cap. 13].
|
|
77
|
+
* El código copia sin traducir el modelo de un proveedor.
|
|
78
|
+
|
|
79
|
+
## Siguiente guía
|
|
80
|
+
|
|
81
|
+
* Estructura interna de cada contexto y dónde vive el ACL → `ddd-skills/ddd-arquitectura/GUIA.md`.
|
|
82
|
+
* Diseño de los eventos que se publican → `ddd-skills/ddd-eventos-dominio/GUIA.md`.
|
|
83
|
+
* Consistencia eventual entre agregados y procesos → `ddd-skills/ddd-agregados/GUIA.md`.
|