@imolko/ultra-agent 0.1.0-beta
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/README.md +93 -0
- package/bin/cli.js +2 -0
- package/dist-node/bin.d.ts +2 -0
- package/dist-node/bin.d.ts.map +1 -0
- package/dist-node/bin.js +5 -0
- package/dist-node/bin.js.map +1 -0
- package/dist-node/cli.d.ts +41 -0
- package/dist-node/cli.d.ts.map +1 -0
- package/dist-node/cli.js +170 -0
- package/dist-node/cli.js.map +1 -0
- package/dist-node/index.d.ts +14 -0
- package/dist-node/index.d.ts.map +1 -0
- package/dist-node/index.js +40 -0
- package/dist-node/index.js.map +1 -0
- package/dist-node/init-lint.d.ts +43 -0
- package/dist-node/init-lint.d.ts.map +1 -0
- package/dist-node/init-lint.js +84 -0
- package/dist-node/init-lint.js.map +1 -0
- package/dist-node/manifest.d.ts +33 -0
- package/dist-node/manifest.d.ts.map +1 -0
- package/dist-node/manifest.js +59 -0
- package/dist-node/manifest.js.map +1 -0
- package/dist-node/materializer.d.ts +52 -0
- package/dist-node/materializer.d.ts.map +1 -0
- package/dist-node/materializer.js +194 -0
- package/dist-node/materializer.js.map +1 -0
- package/dist-node/package-root.d.ts +5 -0
- package/dist-node/package-root.d.ts.map +1 -0
- package/dist-node/package-root.js +17 -0
- package/dist-node/package-root.js.map +1 -0
- package/dist-node/payload.d.ts +22 -0
- package/dist-node/payload.d.ts.map +1 -0
- package/dist-node/payload.js +49 -0
- package/dist-node/payload.js.map +1 -0
- package/dist-node/pointer-hub.d.ts +22 -0
- package/dist-node/pointer-hub.d.ts.map +1 -0
- package/dist-node/pointer-hub.js +50 -0
- package/dist-node/pointer-hub.js.map +1 -0
- package/dist-node/publication.cli.d.ts +9 -0
- package/dist-node/publication.cli.d.ts.map +1 -0
- package/dist-node/publication.cli.js +17 -0
- package/dist-node/publication.cli.js.map +1 -0
- package/dist-node/publication.d.ts +23 -0
- package/dist-node/publication.d.ts.map +1 -0
- package/dist-node/publication.js +47 -0
- package/dist-node/publication.js.map +1 -0
- package/dist-node/reconcile.cli.d.ts +11 -0
- package/dist-node/reconcile.cli.d.ts.map +1 -0
- package/dist-node/reconcile.cli.js +39 -0
- package/dist-node/reconcile.cli.js.map +1 -0
- package/dist-node/reconcile.d.ts +30 -0
- package/dist-node/reconcile.d.ts.map +1 -0
- package/dist-node/reconcile.js +51 -0
- package/dist-node/reconcile.js.map +1 -0
- package/dist-node/ship/prompts/fix.md +22 -0
- package/dist-node/ship/prompts/implement.md +34 -0
- package/dist-node/ship/prompts/review.md +28 -0
- package/dist-node/ship/ship.agent.d.ts +22 -0
- package/dist-node/ship/ship.agent.d.ts.map +1 -0
- package/dist-node/ship/ship.agent.js +142 -0
- package/dist-node/ship/ship.agent.js.map +1 -0
- package/dist-node/ship/ship.args.d.ts +15 -0
- package/dist-node/ship/ship.args.d.ts.map +1 -0
- package/dist-node/ship/ship.args.js +93 -0
- package/dist-node/ship/ship.args.js.map +1 -0
- package/dist-node/ship/ship.context.d.ts +12 -0
- package/dist-node/ship/ship.context.d.ts.map +1 -0
- package/dist-node/ship/ship.context.js +3 -0
- package/dist-node/ship/ship.context.js.map +1 -0
- package/dist-node/ship/ship.d.ts +4 -0
- package/dist-node/ship/ship.d.ts.map +1 -0
- package/dist-node/ship/ship.js +221 -0
- package/dist-node/ship/ship.js.map +1 -0
- package/dist-node/ship/ship.mr.d.ts +9 -0
- package/dist-node/ship/ship.mr.d.ts.map +1 -0
- package/dist-node/ship/ship.mr.js +171 -0
- package/dist-node/ship/ship.mr.js.map +1 -0
- package/dist-node/ship/ship.output.d.ts +9 -0
- package/dist-node/ship/ship.output.d.ts.map +1 -0
- package/dist-node/ship/ship.output.js +21 -0
- package/dist-node/ship/ship.output.js.map +1 -0
- package/dist-node/ship/ship.tracker.d.ts +37 -0
- package/dist-node/ship/ship.tracker.d.ts.map +1 -0
- package/dist-node/ship/ship.tracker.js +220 -0
- package/dist-node/ship/ship.tracker.js.map +1 -0
- package/dist-node/version.cli.d.ts +9 -0
- package/dist-node/version.cli.d.ts.map +1 -0
- package/dist-node/version.cli.js +30 -0
- package/dist-node/version.cli.js.map +1 -0
- package/dist-node/version.d.ts +24 -0
- package/dist-node/version.d.ts.map +1 -0
- package/dist-node/version.js +76 -0
- package/dist-node/version.js.map +1 -0
- package/docs/adr/ADR-0000-proceso-y-formato-de-adrs.md +115 -0
- package/docs/adr/foundational/A-001-clean-architecture-4-capas.md +88 -0
- package/docs/adr/foundational/A-002-cqrs-semantico.md +58 -0
- package/docs/adr/foundational/A-003-tdd.md +97 -0
- package/docs/adr/foundational/A-005-either-return-custom-methods.md +80 -0
- package/docs/adr/foundational/A-006-feature-completion-criteria.md +47 -0
- package/docs/adr/foundational/A-007-data-contract-fail-fast.md +109 -0
- package/docs/adr/foundational/A-008-bounded-context-decomposition.md +130 -0
- package/docs/adr/foundational/A-009-use-case-taxonomy.md +99 -0
- package/docs/adr/foundational/A-010-propiedad-emision-eventos-dominio.md +95 -0
- package/docs/adr/foundational/A-011-characterization-first-refactoring.md +90 -0
- package/docs/adr/foundational/A-012-helm-mecanismo-primario-despliegue.md +67 -0
- package/docs/adr/foundational/A-013-contrato-settings-unica-fuente-configuracion.md +74 -0
- package/docs/adr/foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md +75 -0
- package/docs/adr/foundational/A-015-cloudevents-estandar-eventos.md +85 -0
- package/docs/adr/foundational/A-016-cicd-gitlab-pipeline-raiz-pipelines-modulares.md +92 -0
- package/docs/adr/foundational/A-017-taxonomia-repositorios-librerias-sistemas.md +93 -0
- package/docs/adr/foundational/A-018-practicas-build-imagenes-docker.md +85 -0
- package/docs/adr/foundational/A-019-convenciones-autoria-helm-charts.md +97 -0
- package/docs/adr/foundational/A-020-entorno-desarrollo-estandarizado-devcontainer.md +58 -0
- package/docs/adr/foundational/A-022-formato-reporte-tests-ctrf.md +55 -0
- package/docs/adr/foundational/A-023-composicion-bloques-prompt-agentes.md +65 -0
- package/docs/adr/foundational/A-024-features-slices-verticales-portables.md +41 -0
- package/docs/adr/foundational/A-025-anatomia-mecanica-feature-puertos-adaptadores-drivers.md +104 -0
- package/docs/adr/foundational/A-026-infraestructura-compartida.md +44 -0
- package/docs/adr/foundational/A-027-patron-wiring-entry-point.md +67 -0
- package/docs/adr/foundational/A-029-verbos-intencion-metodos-lado-negocio.md +54 -0
- package/docs/adr/foundational/A-030-global-development-tooling.md +52 -0
- package/docs/adr/foundational/A-031-ubiquitous-language.md +64 -0
- package/docs/adr/foundational/A-032-domain-artifact-documentation-format.md +128 -0
- package/docs/adr/foundational/A-033-estandar-mensajes-commit.md +127 -0
- package/docs/adr/foundational/A-034-canonical-skill-set.md +64 -0
- package/docs/adr/foundational/A-035-captura-ideas-reanclaje-candies.md +51 -0
- package/docs/adr/foundational/A-036-adopcion-wayfinder-ortogonalidad-labels.md +37 -0
- package/docs/adr/foundational/A-037-formalizacion-taxonomia-labels.md +98 -0
- package/docs/adr/foundational/A-038-issues-referencian-skills-nombre.md +54 -0
- package/docs/adr/foundational/A-039-pipeline-ship-issues-hijos-spec.md +64 -0
- package/docs/adr/foundational/A-040-punteros-configuracion-repo-skills.md +44 -0
- package/docs/adr/foundational/A-041-estrategia-de-ramas.md +45 -0
- package/docs/adr/foundational/A-042-formato-documentos-issue-tracker.md +78 -0
- package/docs/adr/foundational/A-043-vocabulario-etiquetas-adrs.md +42 -0
- package/docs/adr/foundational/A-044-composicion-funcional-procesos-operaciones.md +44 -0
- package/docs/adr/foundational/A-045-guardrails-aplicables-no-directrices.md +48 -0
- package/docs/adr/foundational/A-046-metricas-de-forma-y-legibilidad-del-codigo.md +50 -0
- package/docs/adr/foundational/A-047-dependencias-explicitas-sin-globales-ambientales.md +40 -0
- package/docs/adr/foundational/A-048-cobertura-minima-codigo.md +39 -0
- package/docs/adr/foundational/A-049-materializacion-bucle-guardrails-extension-nativa-pi.md +49 -0
- package/docs/adr/foundational/A-050-el-caso-de-uso-pide-al-dominio-no-lee-su-representacion.md +50 -0
- package/docs/adr/foundational/A-051-orden-imports-por-distancia.md +47 -0
- package/docs/adr/foundational/A-052-distribucion-metodologia-paquete-versionado.md +42 -0
- package/docs/adr/foundational/A-053-errores-compartidos-entre-features-dos-hogares-por-capa.md +43 -0
- package/docs/adr/foundational/A-054-contrato-costura-driver-port-raw-data-canal-error.md +65 -0
- package/docs/adr/foundational/index.md +98 -0
- package/docs/adr/index.md +19 -0
- package/docs/adr/python/P-001-dependencias-por-feature-fastapi-depends.md +63 -0
- package/docs/adr/python/P-002-inyeccion-token-por-request-sin-persistirlo.md +35 -0
- package/docs/adr/python/P-003-sqlalchemy-async-connection-pooling.md +59 -0
- package/docs/adr/python/P-004-serializer-json-custom-modelos-pydantic.md +47 -0
- package/docs/adr/python/P-005-patrones-pitfalls-nicegui.md +148 -0
- package/docs/adr/python/P-006-convenciones-pytest.md +96 -0
- package/docs/adr/python/P-007-ci-python-alineacion-poetry-version-unica.md +55 -0
- package/docs/adr/python/P-008-empaquetado-infraestructura-compartida-por-tecnologia.md +38 -0
- package/docs/adr/python/index.md +14 -0
- package/docs/adr/typescript/T-001-ddd-artifact-validation-zod-primitives.md +136 -0
- package/docs/adr/typescript/T-002-test-documentation-jsdoc.md +133 -0
- package/docs/adr/typescript/T-003-readme-formatting-domain-artifacts.md +51 -0
- package/docs/adr/typescript/T-005-input-optionality-use-case-not-entity.md +130 -0
- package/docs/adr/typescript/T-006-small-enums-inlined-zod-regex.md +77 -0
- package/docs/adr/typescript/T-007-controller-input-zod-schema.md +79 -0
- package/docs/adr/typescript/T-008-expressive-use-case-pipe.md +207 -0
- package/docs/adr/typescript/T-009-mdx-brace-escaping.md +80 -0
- package/docs/adr/typescript/T-010-stack-tooling-typescript-dominio.md +53 -0
- package/docs/adr/typescript/T-011-ultra-reporter-herramienta-canonica-documentacion.md +52 -0
- package/docs/adr/typescript/T-012-npm-workspaces-estructura-monorepo-clean-architecture.md +54 -0
- package/docs/adr/typescript/T-013-definicion-eventos-versionados-ultra-ddd.md +45 -0
- package/docs/adr/typescript/T-014-publicacion-distribucion-librerias-npm-semver.md +53 -0
- package/docs/adr/typescript/T-015-method-placement-definition.md +35 -0
- package/docs/adr/typescript/T-016-contrato-settings-zod.md +34 -0
- package/docs/adr/typescript/T-017-characterization-tests-golden-files.md +39 -0
- package/docs/adr/typescript/T-018-procedencia-campos-frontmatter.md +55 -0
- package/docs/adr/typescript/T-019-ultra-generator-herramienta-canonica-generacion.md +35 -0
- package/docs/adr/typescript/T-020-ultra-settings-herramienta-canonica-exportacion-despliegue.md +36 -0
- package/docs/adr/typescript/T-021-pipe-flujo-datos-operadores-semantica-contexto-inmutable.md +105 -0
- package/docs/adr/typescript/T-022-construction-is-execution-single-boundary.md +112 -0
- package/docs/adr/typescript/T-023-registry-cli-declara-dependencias-reales.md +84 -0
- package/docs/adr/typescript/T-024-paquete-ultra-agent-manifiesto-pi-peers-cli.md +39 -0
- package/docs/adr/typescript/T-025-publicacion-npm-trusted-publishing-procedencia.md +59 -0
- package/docs/adr/typescript/index.md +30 -0
- package/eslint/index.mjs +721 -0
- package/package.json +94 -0
- package/pi/extensions/handoff/README.md +92 -0
- package/pi/extensions/handoff/handoff-prompt.txt +9 -0
- package/pi/extensions/handoff/index.ts +296 -0
- package/pi/extensions/handoff/package.json +12 -0
- package/pi/extensions/qa-guardrail/README.md +108 -0
- package/pi/extensions/qa-guardrail/adapters/cli.ts +100 -0
- package/pi/extensions/qa-guardrail/adapters/eslint.ts +90 -0
- package/pi/extensions/qa-guardrail/adapters/registry.ts +45 -0
- package/pi/extensions/qa-guardrail/baseline.ts +74 -0
- package/pi/extensions/qa-guardrail/cerco.ts +134 -0
- package/pi/extensions/qa-guardrail/check.ts +185 -0
- package/pi/extensions/qa-guardrail/config.ts +189 -0
- package/pi/extensions/qa-guardrail/delta.ts +52 -0
- package/pi/extensions/qa-guardrail/feedback.ts +26 -0
- package/pi/extensions/qa-guardrail/index.ts +311 -0
- package/pi/extensions/qa-guardrail/nstop.ts +82 -0
- package/pi/extensions/qa-guardrail/package.json +12 -0
- package/pi/extensions/qa-guardrail/selection.ts +98 -0
- package/pi/extensions/qa-guardrail/types.ts +64 -0
- package/scripts/copy-assets.sh +10 -0
- package/scripts/reconcile-tags.sh +94 -0
- package/scripts/release-beta.sh +64 -0
- package/scripts/release-build.sh +53 -0
- package/scripts/release.sh +51 -0
- package/scripts/verify-adr-consistency.py +217 -0
- package/skills/code-review/SKILL.md +66 -0
- package/skills/codebase-design/SKILL.md +46 -0
- package/skills/configure-repo/SKILL.md +33 -0
- package/skills/diagnose-bug/SKILL.md +63 -0
- package/skills/domain-modeling/SKILL.md +56 -0
- package/skills/grill-me/SKILL.md +34 -0
- package/skills/i-have-an-idea/SKILL.md +166 -0
- package/skills/implement/SKILL.md +22 -0
- package/skills/research/SKILL.md +17 -0
- package/skills/ship/SKILL.md +26 -0
- package/skills/spike/SKILL.md +27 -0
- package/skills/tdd/SKILL.md +68 -0
- package/skills/tdd/references/deep-modules.md +53 -0
- package/skills/tdd/references/interface-design.md +74 -0
- package/skills/tdd/references/mocking.md +80 -0
- package/skills/tdd/references/refactoring.md +49 -0
- package/skills/tdd/references/tests.md +184 -0
- package/skills/to-specs/SKILL.md +141 -0
- package/skills/to-tasks/SKILL.md +143 -0
- package/skills/triage/SKILL.md +73 -0
- package/skills/ultra-generator/SKILL.md +263 -0
- package/skills/ultra-generator/references/aggregate-root.md +149 -0
- package/skills/ultra-generator/references/domain-event.md +174 -0
- package/skills/ultra-generator/references/entity.md +133 -0
- package/skills/ultra-generator/references/service.md +92 -0
- package/skills/ultra-generator/references/value-object.md +135 -0
- package/skills/wayfinder/SKILL.md +201 -0
- package/skills/workflow/SKILL.md +31 -0
- package/skills/writing-for-agents/SKILL-MECHANICS.md +32 -0
- package/skills/writing-for-agents/SKILL.md +86 -0
- package/skills/writing-for-agents/references/adrs.md +19 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# A-003: Desarrollo basado en pruebas (TDD)
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2025-02-25
|
|
6
|
+
**originally**: imolko
|
|
7
|
+
|
|
8
|
+
**tags**: testing
|
|
9
|
+
|
|
10
|
+
## Contexto
|
|
11
|
+
|
|
12
|
+
Las pruebas automatizadas fijan el comportamiento esperado de todo el código del proyecto, en especial de las reglas de negocio complejas: validaciones de value-objects, transiciones de estado de agregados, políticas de negocio, sagas y cálculos de inventario.
|
|
13
|
+
|
|
14
|
+
La forma de escribirlas necesita una decisión canónica que fije el ciclo, las capas, la nomenclatura y la cobertura de cada artefacto.
|
|
15
|
+
|
|
16
|
+
Mientras esa forma queda implícita, cada artefacto prueba a su manera: los errores de dominio se detectan tarde, las regresiones se acumulan al modificar reglas de validación o lógica de agregados y los expertos del dominio pierden visibilidad de qué comportamientos están cubiertos.
|
|
17
|
+
|
|
18
|
+
## Decisión
|
|
19
|
+
|
|
20
|
+
Adoptar **Test-Driven Development (TDD)** con el ciclo **red-green-refactor** como metodología obligatoria para todo el código del proyecto.
|
|
21
|
+
|
|
22
|
+
### Stack de testing
|
|
23
|
+
|
|
24
|
+
El stack concreto de testing (runner, matchers, tipos de error) es específico de cada ecosistema y lo define su tier de lenguaje. Este ADR fija los principios.
|
|
25
|
+
|
|
26
|
+
### Principios
|
|
27
|
+
|
|
28
|
+
1. **Red-Green-Refactor**: escribir un test que falle → implementar el código mínimo → refactorizar
|
|
29
|
+
2. **Slices verticales**: un test → una implementación. No escribir todos los tests primero y luego toda la implementación
|
|
30
|
+
3. **API pública**: los tests verifican comportamiento a través de la API pública del artefacto, no de sus internos
|
|
31
|
+
4. **Tests en el idioma del repo**: los nombres y descripciones de los tests van en el idioma del repo (`issue_language`, [A-052](./A-052-distribucion-metodologia-paquete-versionado.md)), el idioma de los colaboradores y expertos del dominio
|
|
32
|
+
5. **Aserciones semánticas**: usar aserciones que expresen el resultado del dominio (válido / inválido / error) en lugar de asserts genéricos
|
|
33
|
+
6. **Tracer bullet**: el primer test siempre verifica la creación válida del artefacto
|
|
34
|
+
|
|
35
|
+
### Pirámide de tests en tres capas
|
|
36
|
+
|
|
37
|
+
Organizamos los tests en tres capas con propósitos diferenciados. Cada capa responde una pregunta distinta sobre el sistema:
|
|
38
|
+
|
|
39
|
+
| Capa | Propósito | Características |
|
|
40
|
+
|------|-----------|-----------------|
|
|
41
|
+
| **Contract** | Validar interfaces, esquemas y contratos — no comportamiento | Sin dependencias externas, validan estructura de datos y tipos |
|
|
42
|
+
| **Unit** | Lógica de dominio aislada con dependencias mockeadas | Rápidos (milisegundos), deterministas, sin I/O |
|
|
43
|
+
| **Integration** | Features punta a punta con infraestructura real o containerizada | Verifican que los adaptadores cumplen los contratos definidos por los puertos |
|
|
44
|
+
|
|
45
|
+
**Regla de proporción**: la mayoría de los tests deben ser unitarios, seguidos por contract, y la menor cantidad deben ser integration. Los tests de integración son más lentos y frágiles; los unitarios y de contrato dan feedback en segundos.
|
|
46
|
+
|
|
47
|
+
**Hogar de cada capa**: los tests unitarios y de contrato de una feature viven en la feature. El test de integración — el flujo completo caso de uso → adaptador → driver contra un driver real — vive en `shared-infra/<tech>/`, junto al driver que verifica ([A-026](./A-026-infraestructura-compartida.md)); la feature puede exportar una suite de contrato reutilizable que cada driver de `shared-infra/<tech>/` ejecuta contra su propia implementación.
|
|
48
|
+
|
|
49
|
+
### Convenciones de nomenclatura
|
|
50
|
+
|
|
51
|
+
El nombre del archivo de test debe comunicar su capa sin requerir leer su contenido. Usamos un sufijo estandarizado que identifica el tipo de test:
|
|
52
|
+
|
|
53
|
+
- **Contract**: sufijo que denote validación de contrato (ej. `.contract.test.*`, `_contract_test.*`)
|
|
54
|
+
- **Unit**: sufijo estándar de test unitario (ej. `.unit.test.*`, `_test.*` cuando el default es unitario)
|
|
55
|
+
- **Integration**: sufijo que denote integración (ej. `.integration.test.*`, `_integration_test.*`)
|
|
56
|
+
|
|
57
|
+
La convención concreta del sufijo la define el tier de lenguaje de cada ecosistema. Lo importante es que el nombre del archivo distinga la capa.
|
|
58
|
+
|
|
59
|
+
### Separación de runners por entorno de ejecución
|
|
60
|
+
|
|
61
|
+
Cuando un proyecto tiene múltiples entornos de ejecución (ej. browser y servidor), puede ser necesario separar los tests en runners distintos. Cada runner está optimizado para el entorno donde se ejecuta el código:
|
|
62
|
+
|
|
63
|
+
- **Browser/UI**: runner con DOM simulado para componentes y lógica de presentación
|
|
64
|
+
- **Servidor/CLI**: runner con acceso real a sistema de archivos, variables de entorno y APIs del sistema operativo
|
|
65
|
+
|
|
66
|
+
La separación permite que cada entorno use las herramientas nativas que necesita (ej. acceso al sistema de archivos en servidor, DOM simulado en browser) sin comprometer la velocidad ni la fidelidad de los tests. El CI ejecuta ambos runners secuencialmente.
|
|
67
|
+
|
|
68
|
+
**Criterios para separar runners:**
|
|
69
|
+
1. Si el código bajo prueba requiere APIs específicas de un entorno (DOM, filesystem, network sockets), usar el runner de ese entorno
|
|
70
|
+
2. Si el código es puramente lógico y no toca I/O, puede ejecutarse en cualquier runner — preferir el más rápido
|
|
71
|
+
3. Un comando único debe ejecutar todos los runners para feedback completo en CI
|
|
72
|
+
|
|
73
|
+
### Cobertura requerida
|
|
74
|
+
|
|
75
|
+
- **Value-objects**: creación válida, tipo inválido, mínimo/máximo, formato, instancia
|
|
76
|
+
- **Entidades**: creación válida con ID, validaciones de propiedades
|
|
77
|
+
- **Agregados**: creación válida, restauración desde snapshot/memento, transiciones de estado
|
|
78
|
+
- **Servicios de dominio**: casos de éxito, casos de error, edge cases
|
|
79
|
+
- **Features (casos de uso)**: flujo exitoso, errores de dominio, errores de infraestructura
|
|
80
|
+
|
|
81
|
+
### Guía detallada
|
|
82
|
+
|
|
83
|
+
Ver el skill de TDD en el paquete `@imolko/ultra-agent` (la fuente canónica del conjunto, [A-052](./A-052-distribucion-metodologia-paquete-versionado.md)) para patrones de test, ejemplos y referencias.
|
|
84
|
+
|
|
85
|
+
## Consecuencias
|
|
86
|
+
|
|
87
|
+
### Positivas
|
|
88
|
+
|
|
89
|
+
- **Confianza en refactors**: los tests garantizan que los cambios no rompen comportamiento existente
|
|
90
|
+
- **Documentación viva**: los tests describen las reglas de negocio, legibles por expertos del dominio
|
|
91
|
+
- **Detección temprana**: errores de dominio se detectan en segundos, no en días
|
|
92
|
+
- **Diseño guiado por tests**: TDD fuerza pensar en la API desde la perspectiva del consumidor
|
|
93
|
+
|
|
94
|
+
### Negativas
|
|
95
|
+
|
|
96
|
+
- **Tiempo inicial**: escribir tests antes del código requiere más tiempo al inicio (se recupera en mantenimiento)
|
|
97
|
+
- **Disciplina**: requiere que todo el equipo siga la metodología consistentemente
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# A-005: Métodos del lado del negocio retornan `Result` (Either)
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-05-22
|
|
6
|
+
**originally**: imolko
|
|
7
|
+
|
|
8
|
+
**tags**: dominio
|
|
9
|
+
|
|
10
|
+
## Contexto
|
|
11
|
+
|
|
12
|
+
Los procesos y operaciones se componen como un pipeline de pasos encadenados ([A-044](./A-044-composicion-funcional-procesos-operaciones.md)). Para que el pipeline no se interrumpa, cada paso produce un valor para el siguiente; un paso que lanza una excepción rompe la composición y obliga al caller a cablear el control de errores a mano.
|
|
13
|
+
|
|
14
|
+
La construcción de un artefacto de dominio ya retorna un `Result`/`Either` que encapsula la posibilidad de error. Los métodos que se agregan manualmente (como `add()`, `subtract()`, `isExpired()`) necesitan la misma convención de retorno. Apartarse de ella toma dos formas incorrectas:
|
|
15
|
+
|
|
16
|
+
1. **Lanzar excepciones**: el método valida y lanza `throw`; el caller debe envolver cada llamada en try/catch y el pipeline se rompe.
|
|
17
|
+
2. **Desenvolver el `Result`**: el método extrae el valor y descarta el error, forzando al caller a manejar el error fuera del tipo.
|
|
18
|
+
|
|
19
|
+
Cualquiera de las dos impide componer el método dentro del pipeline.
|
|
20
|
+
|
|
21
|
+
## Decisión
|
|
22
|
+
|
|
23
|
+
Todo método del lado del negocio — el dominio y los casos de uso, con sus puertos ([A-029](./A-029-verbos-intencion-metodos-lado-negocio.md)) — debe:
|
|
24
|
+
|
|
25
|
+
1. **Retornar `Result`/`Either`** — el método que produce un valor retorna el error como valor en el canal de error; nunca el valor a secas.
|
|
26
|
+
2. **No desenvolver el `Result` dentro del método** — se retorna íntegro.
|
|
27
|
+
3. **No lanzar excepciones** — ningún método del lado del negocio lanza; el error viaja en el canal de error del `Result`.
|
|
28
|
+
|
|
29
|
+
Este ADR fija *qué* retornan los métodos; *por qué* (componer sin interrupciones) lo fija [A-044](./A-044-composicion-funcional-procesos-operaciones.md). El tipo concreto de `Result` y sus operadores los provee el tier de lenguaje de cada ecosistema.
|
|
30
|
+
|
|
31
|
+
### Regla
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
// ✅ CORRECTO: retorna Result, el caller compone
|
|
35
|
+
restar(otro): Result<Errores, Cantidad> {
|
|
36
|
+
return Cantidad.crear({ valor: nuevo })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ❌ INCORRECTO: desenvuelve el Result (descarta el error)
|
|
40
|
+
restar(otro): Cantidad {
|
|
41
|
+
return Cantidad.crear({ valor: nuevo }).valor
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ❌ INCORRECTO: lanza excepción
|
|
45
|
+
restar(otro): Cantidad {
|
|
46
|
+
si nuevo <= 0: lanzar Error()
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Componer, no desmembrar
|
|
51
|
+
|
|
52
|
+
La decisión fija *qué* retorna el método; *cómo* lo consume el caller importa igual, porque de eso depende que la composición se mantenga ([A-044](./A-044-composicion-funcional-procesos-operaciones.md)):
|
|
53
|
+
|
|
54
|
+
1. **Componer con los operadores del tier** — el caller encadena el `Result` con los operadores que provee el ecosistema; no ramifica sobre los predicados de estado (que son booleanos y no estrechan el tipo).
|
|
55
|
+
2. **Sin extracción forzada** — el valor se obtiene dentro del operador, donde el tipo ya está resuelto; no con aserciones de no-nulo.
|
|
56
|
+
3. **Ensanchar el canal de error por anotación de tipo, no por casts** — cuando un paso produce un error más estrecho que el del proceso, se anota el tipo más amplio en la variable que lo recibe.
|
|
57
|
+
|
|
58
|
+
### Tipos de retorno según la operación
|
|
59
|
+
|
|
60
|
+
| Operación | Tipo de retorno | Justificación |
|
|
61
|
+
|-----------|----------------|---------------|
|
|
62
|
+
| Construcción interna dentro de otro método | `Result<Errores, T>` | La construcción puede fallar; propagar el `Result` |
|
|
63
|
+
| Transformación sin fallo (p. ej. `equals()`, un getter de primitivo) | valor directo (`boolean`, primitivo, …) | No hay posibilidad de error |
|
|
64
|
+
| Validación / consulta con fallo | `Result<Errores, T>` | El fallo es parte del dominio; encapsular en `Result` |
|
|
65
|
+
| Operación aritmética cuyo resultado debe cumplir invariantes | `Result<Errores, T>` | El resultado se construye con un constructor que ya retorna `Result` |
|
|
66
|
+
|
|
67
|
+
## Consecuencias
|
|
68
|
+
|
|
69
|
+
### Positivas
|
|
70
|
+
|
|
71
|
+
- **Componibilidad total**: cualquier método se integra en el pipeline sin adaptadores.
|
|
72
|
+
- **Sin excepciones**: los errores de dominio son valores, no saltos de pila — facilita testing y razonamiento.
|
|
73
|
+
- **Consistencia con el constructor**: mismo patrón que la construcción del artefacto.
|
|
74
|
+
- **Errores declarados**: el tipo `Result` documenta todos los fallos posibles.
|
|
75
|
+
- **Testabilidad**: los tests usan aserciones semánticas (válido/inválido); el detalle concreto lo define el tier de lenguaje (integrado en el ciclo TDD, [A-003](./A-003-tdd.md)).
|
|
76
|
+
|
|
77
|
+
### Negativas
|
|
78
|
+
|
|
79
|
+
- **Verbosidad en callers simples**: si el caller solo necesita el valor, debe manejar el `Result` explícitamente. En la práctica es poco frecuente porque la arquitectura favorece pipelines.
|
|
80
|
+
- **Dependencia del tipo `Result`**: cada método debe importar el tipo y los errores declarados del artefacto.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# A-006: Criterios de completitud de artefactos de dominio y features (definition of done)
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-05-27
|
|
6
|
+
**originally**: imolko
|
|
7
|
+
|
|
8
|
+
**tags**: testing
|
|
9
|
+
|
|
10
|
+
## Contexto
|
|
11
|
+
|
|
12
|
+
Un artefacto de dominio o feature se completa cerrando tres frentes a la vez: la lógica (código y reglas de negocio), la prueba (tests que fijan el comportamiento esperado) y la documentación (el formato canónico de [A-032](./A-032-domain-artifact-documentation-format.md)). Cuando una herramienta de scaffolding lo genera, añade además material de construcción — placeholders e instrucciones de implementación — que guía la implementación pero no es parte del artefacto terminado.
|
|
13
|
+
|
|
14
|
+
La completitud necesita una decisión canónica que fije cuándo un artefacto se declara terminado: qué criterios aplican y cómo se verifican.
|
|
15
|
+
|
|
16
|
+
Mientras esa decisión queda implícita, la completitud se confunde con una señal aislada — **tests verdes** se lee como "terminado" — y quedan sin cerrar la documentación y sin retirar el material de construcción. El artefacto funciona, pero no está documentado para el equipo y arrastra material de construcción ajeno.
|
|
17
|
+
|
|
18
|
+
## Decisión
|
|
19
|
+
|
|
20
|
+
Un artefacto de dominio o feature se considera **completo** únicamente cuando cumple **todos los criterios de completitud aplicables**, cada uno verificable de forma explícita. No importa quién lo produjo — herramienta de scaffolding, agente o persona. La completitud es un estado **binario y verificable criterio por criterio**, no una señal aislada. El orden de implementación es libre; ningún criterio aplicable puede omitirse.
|
|
21
|
+
|
|
22
|
+
Los criterios concretos los fijan sus ADRs propietarios; este ADR los referencia, no los re-decide:
|
|
23
|
+
|
|
24
|
+
| Frente | Criterio | Lo fija |
|
|
25
|
+
|--------|----------|---------|
|
|
26
|
+
| Lógica | El código compila y los tests pasan | [A-003](./A-003-tdd.md); lo verificable por máquina, [A-045](./A-045-guardrails-aplicables-no-directrices.md) |
|
|
27
|
+
| Flujo | El caso de uso se expresa como composición de pasos | [A-044](./A-044-composicion-funcional-procesos-operaciones.md) |
|
|
28
|
+
| Documentación | Formato y frontmatter canónicos, sin placeholders | [A-032](./A-032-domain-artifact-documentation-format.md) |
|
|
29
|
+
| Scaffolding | El material de construcción no sobrevive al artefacto terminado | el tier de lenguaje define el archivo concreto y su eliminación |
|
|
30
|
+
|
|
31
|
+
Cada criterio se verifica de forma explícita: por máquina (compila, tests verdes, scaffolding ausente) o por revisión (flujo, documentación).
|
|
32
|
+
|
|
33
|
+
## Consecuencias
|
|
34
|
+
|
|
35
|
+
### Positivas
|
|
36
|
+
|
|
37
|
+
- **"Terminado" no ambiguo**: cualquier productor — herramienta, agente o persona — dispone de una lista de criterios aplicables para declarar un artefacto completo.
|
|
38
|
+
- **Documentación siempre cerrada**: al ser parte de la completitud, el artefacto terminado describe su dominio para el equipo ([A-032](./A-032-domain-artifact-documentation-format.md)).
|
|
39
|
+
- **Sin material de construcción residual**: el scaffolding no sobrevive al artefacto terminado.
|
|
40
|
+
- **Verificación explícita**: cada criterio se comprueba por máquina o por revisión ([A-045](./A-045-guardrails-aplicables-no-directrices.md)), no por la memoria del productor.
|
|
41
|
+
- **Cierre del ciclo**: la completitud cierra la brecha entre "el código funciona" y "el artefacto está terminado para el equipo".
|
|
42
|
+
|
|
43
|
+
### Negativas
|
|
44
|
+
|
|
45
|
+
- **Trabajo manual**: completar la documentación es redacción humana, no generación automática ([A-032](./A-032-domain-artifact-documentation-format.md)).
|
|
46
|
+
- **Disciplina**: el equipo debe verificar todos los criterios aplicables antes de declarar el artefacto completo.
|
|
47
|
+
- **El productor aún puede detenerse antes**: si el issue no menciona la completitud, el agente puede detenerse en tests verdes; se mitiga referenciando el skill por nombre en el issue ([A-038](./A-038-issues-referencian-skills-nombre.md)).
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# A-007: Contrato de datos: fallo en origen, sin fallbacks silenciosos
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: cortex-035
|
|
8
|
+
|
|
9
|
+
**Enriquecido desde**: Cortex ADR 035 — Data Contract Fail-Fast Policy
|
|
10
|
+
|
|
11
|
+
**tags**: testing
|
|
12
|
+
|
|
13
|
+
## Contexto
|
|
14
|
+
|
|
15
|
+
Un caso de uso consume dependencias de datos: valores que necesita para producir un
|
|
16
|
+
resultado correcto. Una dependencia requerida se satisface en origen, o el caso de uso no
|
|
17
|
+
se ejecuta.
|
|
18
|
+
|
|
19
|
+
El tratamiento de una dependencia insatisfecha necesita una decisión canónica: qué se hace
|
|
20
|
+
cuando el dato requerido no está disponible.
|
|
21
|
+
|
|
22
|
+
Mientras esa decisión queda implícita, cada capa agrega su propio aterrizaje suave — valores
|
|
23
|
+
por defecto, advertencias y continuación — y el efecto acumulativo es que un caso de uso
|
|
24
|
+
reporta ÉXITO con resultados incorrectos o sin sentido. Ninguna capa confía en la capa
|
|
25
|
+
superior; una pérdida de datos produce solo una advertencia en el log.
|
|
26
|
+
|
|
27
|
+
## Decisión
|
|
28
|
+
|
|
29
|
+
Una dependencia de datos requerida no satisfecha **falla en origen**: sin fallbacks
|
|
30
|
+
silenciosos ni opcionales por defecto. Un caso de uso que reporta ÉXITO garantiza que todos
|
|
31
|
+
sus contratos de datos fueron honrados; no existe el éxito parcial con datos faltantes.
|
|
32
|
+
|
|
33
|
+
### 1. Fail-fast: sin fallbacks silenciosos
|
|
34
|
+
|
|
35
|
+
Cuando una dependencia de datos requerida no puede satisfacerse, el sistema falla
|
|
36
|
+
inmediatamente. No se admite:
|
|
37
|
+
|
|
38
|
+
- Capturar el error y continuar
|
|
39
|
+
- Advertir y continuar
|
|
40
|
+
- Retornar colecciones vacías en caso de error
|
|
41
|
+
- Reemplazar el dato ausente por un valor por defecto
|
|
42
|
+
|
|
43
|
+
Los errores se propagan por el canal de error del ecosistema. No se crean canales nuevos.
|
|
44
|
+
|
|
45
|
+
### 2. Modelos de concepto, no bolsas de datos
|
|
46
|
+
|
|
47
|
+
Un modelo de dominio representa un concepto de negocio del lenguaje ubicuo
|
|
48
|
+
([A-031](./A-031-ubiquitous-language.md)); un modelo genérico con campos mayormente
|
|
49
|
+
opcionales es un DTO de transporte, no un modelo de dominio.
|
|
50
|
+
|
|
51
|
+
### 3. No-opcional-por-defecto
|
|
52
|
+
|
|
53
|
+
Los campos son requeridos a menos que la realidad del negocio justifique un campo opcional,
|
|
54
|
+
con la razón documentada en el modelo. Un campo que "usualmente está presente pero a veces
|
|
55
|
+
falta" es requerido: la ausencia se maneja explícitamente en el llamador, no se oculta con
|
|
56
|
+
un valor por defecto.
|
|
57
|
+
|
|
58
|
+
### 4. Invariantes en el momento de construcción
|
|
59
|
+
|
|
60
|
+
Las invariantes de negocio se aplican al construir el modelo, no en métodos de validación
|
|
61
|
+
separados ni en verificaciones posteriores:
|
|
62
|
+
|
|
63
|
+
- Campos requeridos sin valor por defecto
|
|
64
|
+
- Colecciones no vacías como restricción de construcción
|
|
65
|
+
- Números positivos donde el negocio lo exige
|
|
66
|
+
- Validaciones entre campos
|
|
67
|
+
|
|
68
|
+
La construcción falla inmediatamente si una invariante no se cumple. Un modelo no puede
|
|
69
|
+
existir en un estado inválido: no hay ventana de "construir primero, validar después". La
|
|
70
|
+
verificación de estas invariantes sigue el ciclo TDD ([A-003](./A-003-tdd.md)).
|
|
71
|
+
|
|
72
|
+
### 5. El consumidor confía en el contrato, verificado por máquina
|
|
73
|
+
|
|
74
|
+
La declaración de dependencias de un consumidor — p. ej. la plantilla de un motor de
|
|
75
|
+
renderizado — es su contrato de datos. El consumidor no protege con condicionales un dato
|
|
76
|
+
declarado como requerido: el condicional sobre un dato requerido oculta una violación de
|
|
77
|
+
contrato y se trata como defecto en revisión. El condicional solo es aceptable para conceptos
|
|
78
|
+
genuinamente opcionales con su razón documentada.
|
|
79
|
+
|
|
80
|
+
Cuando el consumidor es declarativo — declara sus dependencias y las referencia en su cuerpo,
|
|
81
|
+
p. ej. una plantilla — la verificación es bidireccional y por máquina, como guardrail
|
|
82
|
+
([A-045](./A-045-guardrails-aplicables-no-directrices.md)): cada dependencia declarada está
|
|
83
|
+
presente en el contexto, y cada variable referenciada tiene una dependencia declarada. La
|
|
84
|
+
materialización concreta de la verificación es del tier de lenguaje.
|
|
85
|
+
|
|
86
|
+
## Consecuencias
|
|
87
|
+
|
|
88
|
+
### Positivas
|
|
89
|
+
|
|
90
|
+
- **Resultados confiables**: ÉXITO significa que se usaron los datos correctos.
|
|
91
|
+
- **Detección temprana**: las violaciones de contrato se detectan en el punto más temprano
|
|
92
|
+
posible (la construcción del modelo).
|
|
93
|
+
- **Sin aterrizajes suaves en cascada**: una falla en el origen, no valores vacíos
|
|
94
|
+
propagándose por N capas.
|
|
95
|
+
- **Contrato auto-documentado**: la declaración de dependencias del consumidor es el contrato
|
|
96
|
+
de datos.
|
|
97
|
+
- **Testing acotado**: los modelos con restricciones de "no vacío" fallan de forma predecible;
|
|
98
|
+
no hay necesidad de probar casos de datos vacíos en cada capa ([A-003](./A-003-tdd.md)).
|
|
99
|
+
|
|
100
|
+
### Negativas
|
|
101
|
+
|
|
102
|
+
- **Todo campo requerido debe tener datos**: no hay "empezar vacío y ver qué pasa".
|
|
103
|
+
- **Los datos deben estar disponibles antes de que el caso de uso se ejecute**: siempre fue
|
|
104
|
+
cierto, ahora está aplicado.
|
|
105
|
+
- **Migración**: los modelos existentes con valores por defecto deben auditarse y
|
|
106
|
+
actualizarse.
|
|
107
|
+
- **El autor del consumidor mantiene la declaración en sincronía**: las dependencias
|
|
108
|
+
declaradas deben coincidir con las referencias en el cuerpo (aplicado por la verificación
|
|
109
|
+
bidireccional).
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# A-008: Descomposición en Bounded Contexts
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: sentinel-0001
|
|
8
|
+
|
|
9
|
+
**Enriquecido desde**: Sentinel ADR 0001 — Descomposición en Bounded Contexts del dominio
|
|
10
|
+
|
|
11
|
+
**tags**: arquitectura, dominio
|
|
12
|
+
|
|
13
|
+
## Contexto
|
|
14
|
+
|
|
15
|
+
Un dominio DDD con múltiples artefactos (agregados, entidades, value objects, servicios,
|
|
16
|
+
eventos, casos de uso) requiere una organización que permita navegar y comprender el sistema.
|
|
17
|
+
La asignación de cada artefacto a un Bounded Context es necesaria para generar documentación
|
|
18
|
+
estructurada y facilitar el descubrimiento.
|
|
19
|
+
|
|
20
|
+
Sin embargo, no todos los artefactos tienen el mismo peso semántico. Algunos son
|
|
21
|
+
value objects genéricos (validaciones de formato, tipos primitivos enriquecidos) que
|
|
22
|
+
carecen de lógica de dominio específica. Otros forman agrupaciones naturales alrededor
|
|
23
|
+
de un agregado raíz. Tratar todos los artefactos como iguales y asignarlos a un único
|
|
24
|
+
contexto plano oscurece la estructura real del dominio.
|
|
25
|
+
|
|
26
|
+
Se necesita un criterio sistemático para identificar agrupaciones naturales (Bounded
|
|
27
|
+
Contexts) y asignar cada artefacto al contexto que le corresponde.
|
|
28
|
+
|
|
29
|
+
## Decisión
|
|
30
|
+
|
|
31
|
+
### 1. Identificar agrupaciones naturales
|
|
32
|
+
|
|
33
|
+
Un Bounded Context se identifica respondiendo a la pregunta: **"¿qué grupo de artefactos
|
|
34
|
+
gira alrededor de un mismo agregado raíz o concepto central?"**
|
|
35
|
+
|
|
36
|
+
Cada Bounded Context debe tener:
|
|
37
|
+
|
|
38
|
+
- Un **agregado raíz** o concepto central que le da identidad
|
|
39
|
+
- Artefactos que **dependen principalmente** de otros artefactos dentro del mismo contexto
|
|
40
|
+
|
|
41
|
+
Los contexts de un mismo sistema comparten el lenguaje ubicuo del sistema (ver
|
|
42
|
+
[A-031](./A-031-ubiquitous-language.md)); lo que los separa es la cohesión alrededor de
|
|
43
|
+
un agregado raíz.
|
|
44
|
+
|
|
45
|
+
Síntomas de que dos artefactos pertenecen a contexts distintos:
|
|
46
|
+
- Referencian conceptos de negocio fundamentalmente diferentes
|
|
47
|
+
- Cambian por razones diferentes (diferentes stakeholders, diferentes ritmos de cambio)
|
|
48
|
+
|
|
49
|
+
### 2. Artefactos genéricos de bajo valor semántico → contexto compartido
|
|
50
|
+
|
|
51
|
+
Los artefactos que implementan validaciones puras (regex, min/max, enum, formato) son
|
|
52
|
+
utilidad sin lógica de dominio: se agrupan en un contexto compartido (p. ej.
|
|
53
|
+
`shared-kernel`) que refleja su naturaleza de utilidad compartida. `scope::core`
|
|
54
|
+
([A-009](./A-009-use-case-taxonomy.md)) designa la criticidad de un caso de uso, un eje
|
|
55
|
+
independiente del Bounded Context.
|
|
56
|
+
|
|
57
|
+
Características de los artefactos del contexto compartido:
|
|
58
|
+
- Validan solo formato
|
|
59
|
+
- Son reusables entre los demás contexts
|
|
60
|
+
- Son análogos a tipos primitivos enriquecidos (UUID, números naturales, schemas)
|
|
61
|
+
|
|
62
|
+
El contexto compartido funciona como un **Shared Kernel**: artefactos compartidos entre
|
|
63
|
+
contexts que no pertenecen exclusivamente a ninguno de ellos.
|
|
64
|
+
|
|
65
|
+
### 3. Artefactos compartidos: documentar la dependencia
|
|
66
|
+
|
|
67
|
+
Cuando un artefacto del contexto compartido es consumido por artefactos en otros contexts,
|
|
68
|
+
los artefactos consumidores documentan la dependencia en su documentación — el frontmatter
|
|
69
|
+
y la sección `Relaciones` siguen
|
|
70
|
+
[A-032](./A-032-domain-artifact-documentation-format.md). El artefacto compartido no
|
|
71
|
+
referencia a sus consumidores.
|
|
72
|
+
|
|
73
|
+
### 4. Proceso de asignación
|
|
74
|
+
|
|
75
|
+
Para asignar artefactos a Bounded Contexts:
|
|
76
|
+
|
|
77
|
+
1. **Identificar los agregados raíz**: ¿cuáles son las entidades principales del dominio
|
|
78
|
+
que actúan como raíz de agregados?
|
|
79
|
+
2. **Agrupar dependencias**: para cada agregado raíz, listar todos los artefactos que
|
|
80
|
+
dependen de él o que este referencia directamente.
|
|
81
|
+
3. **Identificar artefactos huérfanos**: artefactos que no dependen de ningún agregado
|
|
82
|
+
raíz y que son usados por múltiples contexts → candidatos al contexto compartido.
|
|
83
|
+
4. **Verificar cohesión**: cada contexto debe tener alta cohesión interna (los artefactos
|
|
84
|
+
dentro del contexto se referencian entre sí con frecuencia) y bajo acoplamiento externo
|
|
85
|
+
(las dependencias entre contexts son mínimas y documentadas).
|
|
86
|
+
5. **Nombrar el contexto**: usar un nombre que refleje el concepto central, no la
|
|
87
|
+
implementación.
|
|
88
|
+
|
|
89
|
+
Criterios adicionales:
|
|
90
|
+
- Los casos de uso se asignan al mismo contexto que el agregado que modifican o consultan
|
|
91
|
+
- Los eventos de dominio se asignan al contexto del agregado cuyos datos describen (quién
|
|
92
|
+
los emite y publica lo define [A-010](./A-010-propiedad-emision-eventos-dominio.md))
|
|
93
|
+
- Si un artefacto depende de dos agregados raíz, revisar si el diseño de bounded contexts
|
|
94
|
+
es correcto o si se necesita una interfaz/contrato entre contexts
|
|
95
|
+
|
|
96
|
+
### 5. Separación de responsabilidades
|
|
97
|
+
|
|
98
|
+
Cada Bounded Context tiene un propósito documentado que responde a la pregunta: "¿qué
|
|
99
|
+
parte del dominio modela este contexto?"
|
|
100
|
+
|
|
101
|
+
Ejemplo genérico de propósitos:
|
|
102
|
+
|
|
103
|
+
| Contexto | Propósito |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `shared-kernel` | Validaciones genéricas reusables entre contexts (utilidad compartida) |
|
|
106
|
+
| `contexto-a` | Gestión del ciclo de vida de la entidad central A y sus dependencias directas |
|
|
107
|
+
| `contexto-b` | Gestión de la entidad B, sus recursos asociados e integraciones externas |
|
|
108
|
+
|
|
109
|
+
## Consecuencias
|
|
110
|
+
|
|
111
|
+
**Positivas:**
|
|
112
|
+
|
|
113
|
+
- Separación clara de responsabilidades dentro del dominio
|
|
114
|
+
- La documentación generada se organiza en secciones navegables por contexto
|
|
115
|
+
- Facilita el descubrimiento: un desarrollador nuevo puede identificar rápidamente en qué
|
|
116
|
+
contexto se encuentra lo que busca
|
|
117
|
+
- Proporciona criterios objetivos para decidir dónde reside cada artefacto
|
|
118
|
+
|
|
119
|
+
**Negativas:**
|
|
120
|
+
|
|
121
|
+
- El contexto compartido carece de agregado raíz e identidad de negocio: es una agrupación
|
|
122
|
+
de utilidad, y tratarla como un Bounded Context pleno confunde
|
|
123
|
+
- Añade contextos que pueden parecer artificiales si el dominio es pequeño
|
|
124
|
+
|
|
125
|
+
**Neutrales:**
|
|
126
|
+
|
|
127
|
+
- Los Bounded Contexts son solo una separación lógica y conceptual: no tienen efecto sobre
|
|
128
|
+
la estructura de archivos del repositorio ni sobre las dependencias del código.
|
|
129
|
+
- La herramienta de generación de documentación puede usar los Bounded Contexts como criterio
|
|
130
|
+
de agrupación sin que el sistema de build los conozca
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# A-009: Taxonomía de criticidad de casos de uso
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-07
|
|
6
|
+
|
|
7
|
+
**originally**: sentinel-0004
|
|
8
|
+
|
|
9
|
+
**tags**: arquitectura, dominio
|
|
10
|
+
|
|
11
|
+
## Contexto
|
|
12
|
+
|
|
13
|
+
Los casos de uso de un dominio tienen diferentes niveles de criticidad y propósitos. Sin una taxonomía explícita, la clasificación es inconsistente entre desarrolladores y no hay criterios objetivos para decidir qué casos de uso son esenciales y cuáles son auxiliares. Se necesita una taxonomía de criticidad que asigne a cada caso de uso un nivel y permita filtrarlo y priorizarlo según el interés del stakeholder.
|
|
14
|
+
|
|
15
|
+
## Decisión
|
|
16
|
+
|
|
17
|
+
### 1. Taxonomía de criticidad: `scope::`
|
|
18
|
+
|
|
19
|
+
Cada caso de uso se clasifica en exactamente uno de tres niveles de criticidad — `scope::core`, `scope::support` o `scope::configuration` — expresados como un tag con prefijo `scope::` en el frontmatter del feature (esquema en [A-032](./A-032-domain-artifact-documentation-format.md)).
|
|
20
|
+
|
|
21
|
+
### 2. Definiciones explícitas
|
|
22
|
+
|
|
23
|
+
#### `scope::core`
|
|
24
|
+
|
|
25
|
+
Casos de uso que implementan el propósito fundamental del dominio. Si un caso de uso `core` falla o no existe, el dominio **no puede cumplir su función principal**.
|
|
26
|
+
|
|
27
|
+
Características:
|
|
28
|
+
- Representan el flujo de valor primario del dominio
|
|
29
|
+
- Su ausencia es un bloqueo para la puesta en producción
|
|
30
|
+
- Son los primeros en implementarse y los últimos en desactivarse
|
|
31
|
+
- Típicamente representan la mayoría de los casos de uso (~50-70%)
|
|
32
|
+
|
|
33
|
+
Ejemplos genéricos:
|
|
34
|
+
- Registrar una entidad central del dominio
|
|
35
|
+
- Procesar un evento entrante del exterior
|
|
36
|
+
- Validar una operación contra reglas de negocio
|
|
37
|
+
- Ejecutar una transacción que mueve valor entre agregados
|
|
38
|
+
|
|
39
|
+
#### `scope::support`
|
|
40
|
+
|
|
41
|
+
Casos de uso que mejoran la experiencia, habilitan optimizaciones o proporcionan información adicional. El dominio **puede operar** sin ellos, aunque con funcionalidad reducida o menor eficiencia.
|
|
42
|
+
|
|
43
|
+
Características:
|
|
44
|
+
- No son necesarios para el flujo de valor primario
|
|
45
|
+
- Su ausencia degrada la experiencia pero no bloquea la operación
|
|
46
|
+
- Pueden implementarse en iteraciones posteriores al MVP
|
|
47
|
+
|
|
48
|
+
Ejemplos genéricos:
|
|
49
|
+
- Filtrar entidades por criterios secundarios
|
|
50
|
+
- Validar permisos granulares (más allá de la autorización básica)
|
|
51
|
+
- Generar reportes o vistas resumidas
|
|
52
|
+
- Notificar a usuarios sobre eventos no críticos
|
|
53
|
+
|
|
54
|
+
#### `scope::configuration`
|
|
55
|
+
|
|
56
|
+
Casos de uso que configuran el sistema para que los casos `core` y `support` puedan operar. Se ejecutan típicamente durante el onboarding, setup inicial o mantenimiento administrativo.
|
|
57
|
+
|
|
58
|
+
Características:
|
|
59
|
+
- No se ejecutan durante la operación normal del dominio
|
|
60
|
+
- Su frecuencia de ejecución es baja (una vez por tenant, una vez por release, etc.)
|
|
61
|
+
- Frecuentemente requieren privilegios elevados o contexto de administrador
|
|
62
|
+
- Su ausencia no bloquea la operación de un sistema ya configurado
|
|
63
|
+
|
|
64
|
+
Ejemplos genéricos:
|
|
65
|
+
- Registrar un nuevo tenant o cliente en el sistema
|
|
66
|
+
- Configurar reglas de autorización o políticas
|
|
67
|
+
- Definir esquemas de datos o estructuras que otros casos de uso consumen
|
|
68
|
+
- Establecer parámetros de integración con sistemas externos
|
|
69
|
+
|
|
70
|
+
### 3. Criterios de clasificación
|
|
71
|
+
|
|
72
|
+
Para clasificar un caso de uso, responder las siguientes preguntas en orden:
|
|
73
|
+
|
|
74
|
+
1. **¿Se ejecuta durante la operación normal del dominio?**
|
|
75
|
+
- NO → `scope::configuration`
|
|
76
|
+
- SÍ → continuar a la pregunta 2
|
|
77
|
+
|
|
78
|
+
2. **¿Puede el dominio cumplir su propósito principal sin este caso de uso?**
|
|
79
|
+
- NO → `scope::core`
|
|
80
|
+
- SÍ (con degradación) → `scope::support`
|
|
81
|
+
|
|
82
|
+
En caso de ambigüedad, se asigna el nivel más alto (configuration < support < core).
|
|
83
|
+
|
|
84
|
+
## Consecuencias
|
|
85
|
+
|
|
86
|
+
### Positivas
|
|
87
|
+
|
|
88
|
+
- **Clasificación explícita y documentada**: cualquier desarrollador puede clasificar nuevos casos de uso consistentemente usando los criterios definidos
|
|
89
|
+
- **Priorización objetiva**: la taxonomía permite decidir qué implementar primero (core antes que support, support antes que configuration)
|
|
90
|
+
- **Generación de vistas**: herramientas de reporting pueden filtrar por scope para generar vistas enfocadas ("mostrar solo casos de uso core", "mostrar solo casos de uso de configuración")
|
|
91
|
+
- **Aliado con el lenguaje ubicuo**: los niveles de criticidad reflejan cómo el domain expert piensa sobre las operaciones del sistema
|
|
92
|
+
|
|
93
|
+
### Negativas
|
|
94
|
+
|
|
95
|
+
- **Subjetividad en los bordes**: la clasificación `scope::*` puede ser ambigua en casos límite; requiere revisión del domain expert y aplicación de la regla de desempate (asignar el nivel más alto)
|
|
96
|
+
|
|
97
|
+
### Neutrales
|
|
98
|
+
|
|
99
|
+
- El prefijo `scope::` se eligió para evitar colisiones con otros tags que puedan agregarse en el futuro
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# A-010: Propiedad de la emisión de eventos de dominio
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: cortex-037
|
|
8
|
+
|
|
9
|
+
**Enriquecido desde**: Cortex ADR 037 — Driver-owned event emission
|
|
10
|
+
|
|
11
|
+
**tags**: arquitectura, eventos
|
|
12
|
+
|
|
13
|
+
## Contexto
|
|
14
|
+
|
|
15
|
+
Un caso de uso que cambia el estado del dominio hace dos cosas: persiste el cambio y
|
|
16
|
+
publica los eventos de dominio que lo registran. La pregunta es quién posee cada paso, y
|
|
17
|
+
dos patrones fallan por extremos opuestos:
|
|
18
|
+
|
|
19
|
+
- **El caso de uso construye el evento**: llama al puerto de repositorio, recibe un evento
|
|
20
|
+
como valor de retorno — o lo reconstruye a partir del input — y lo publica. El caso de uso
|
|
21
|
+
termina construyendo hechos sobre una mutación que el agregado ejecutó, sin conocer el
|
|
22
|
+
estado resultante ni las invariantes.
|
|
23
|
+
- **El driver emite el evento**: el driver de repositorio emite eventos como efecto
|
|
24
|
+
secundario de la persistencia. Esto acopla el hecho de dominio a la tecnología de
|
|
25
|
+
persistencia y hace que el puerto asuma emisión de eventos.
|
|
26
|
+
|
|
27
|
+
La propiedad correcta es la de DDD: el **agregado** emite el evento porque posee la
|
|
28
|
+
transición de estado; el **caso de uso** lo publica porque posee la orquestación; el
|
|
29
|
+
**driver** lo entrega porque posee el mecanismo.
|
|
30
|
+
|
|
31
|
+
## Decisión
|
|
32
|
+
|
|
33
|
+
### 1. El agregado emite el hecho
|
|
34
|
+
|
|
35
|
+
Cuando un método del agregado transiciona estado (p. ej. `crear_entidad`), el agregado
|
|
36
|
+
levanta el evento de dominio correspondiente con la data del estado resultante. El evento
|
|
37
|
+
es un artefacto del dominio: su identificador de agregado y su data nacen del agregado, no
|
|
38
|
+
del input del caso de uso ni del valor de retorno del repositorio.
|
|
39
|
+
|
|
40
|
+
### 2. El caso de uso publica, no construye
|
|
41
|
+
|
|
42
|
+
El caso de uso orquesta: carga el agregado, invoca su método (con verbos de intención según
|
|
43
|
+
[A-029](./A-029-verbos-intencion-metodos-lado-negocio.md)), lo persiste a través del puerto de
|
|
44
|
+
repositorio y publica los eventos que el agregado levantó a través del puerto publicador de
|
|
45
|
+
eventos. El caso de uso no reconstruye eventos a partir del input ni de valores de retorno. En persistencia event-sourced, el caso de uso no publica por separado: el append al EventStore es la publicación (ver §5).
|
|
46
|
+
|
|
47
|
+
### 3. El driver entrega, no posee el hecho
|
|
48
|
+
|
|
49
|
+
- El puerto de repositorio persiste estado y retorna agregados o identificadores — nunca
|
|
50
|
+
eventos. (En persistencia event-sourced, persiste apendando los eventos del agregado; ver §5.)
|
|
51
|
+
- El puerto publicador de eventos declara la publicación; el driver que lo implementa posee
|
|
52
|
+
el mecanismo de entrega (un bus en memoria para tests, un transporte real en producción).
|
|
53
|
+
El formato del evento es siempre CloudEvents
|
|
54
|
+
([A-015](./A-015-cloudevents-estandar-eventos.md)), independiente del transporte.
|
|
55
|
+
- La atomicidad entre persistir y publicar es una responsabilidad de entrega: se resuelve en
|
|
56
|
+
el driver del bus, no moviendo el hecho al driver de repositorio. (En persistencia event-sourced, se resuelve dentro del store; ver §5.)
|
|
57
|
+
|
|
58
|
+
### 4. La publicación es fail-fast
|
|
59
|
+
|
|
60
|
+
La publicación de eventos es parte de la operación del caso de uso: no hay publicación
|
|
61
|
+
parcial — o la mutación persiste y sus eventos se publican, o la operación falla — conforme a
|
|
62
|
+
[A-007](./A-007-data-contract-fail-fast.md).
|
|
63
|
+
|
|
64
|
+
### 5. Persistencia event-sourced
|
|
65
|
+
|
|
66
|
+
Cuando la persistencia es event-sourced — el stream de eventos es la fuente de verdad y el estado se reconstruye por replay — los puntos 2 a 4 se materializan así, sin cambiar la propiedad del hecho (el agregado emite, el driver entrega):
|
|
67
|
+
|
|
68
|
+
- **El puerto de repositorio apende los eventos.** El append al EventStore es la **publicación durable**: no hay un paso separado de publicación en el caso de uso; el puerto de repositorio persiste el hecho como eventos, no como estado derivado.
|
|
69
|
+
- **La entrega a suscriptores es un outbox o tail del stream**, poseído por el driver del store: proyecciones, integraciones y notificaciones consumen del stream, no de un segundo canal coordinado.
|
|
70
|
+
- **La atomicidad entre persistir y publicar se resuelve dentro del store**: el append es el hecho atómico; la publicación fail-fast (§4) se cumple porque persistir y publicar son la misma escritura.
|
|
71
|
+
- **El stream se firma y verifica.** Los eventos se firman y el EventStore los verifica al apender y al cargar; el snapshot cache (optimización del store) se firma y verifica al cargar, y se reconstruye desde el stream si su firma no valida.
|
|
72
|
+
|
|
73
|
+
## Consecuencias
|
|
74
|
+
|
|
75
|
+
### Positivas
|
|
76
|
+
|
|
77
|
+
- **El hecho vive en el dominio**: el evento nace del agregado con la data del estado
|
|
78
|
+
resultante; el caso de uso y el driver no reconstruyen hechos ajenos.
|
|
79
|
+
- **Puertos sin filtraciones**: el repositorio retorna agregados, no eventos; el puerto
|
|
80
|
+
publicador solo declara publicación.
|
|
81
|
+
- **Entrega intercambiable**: cambiar la tecnología de entrega (un bus en memoria, NATS,
|
|
82
|
+
RabbitMQ o Kafka) toca solo el driver del bus.
|
|
83
|
+
- **Atomicidad localizada**: el driver del bus resuelve la atomicidad entre persistir y
|
|
84
|
+
publicar; la persistencia de estado no se acopla a la emisión.
|
|
85
|
+
- **Tests simples**: se verifica que el agregado levantó el evento y que el caso de uso lo
|
|
86
|
+
publicó, no la construcción de eventos desde el input.
|
|
87
|
+
|
|
88
|
+
### Negativas
|
|
89
|
+
|
|
90
|
+
- **El agregado debe levantar eventos**: los agregados existentes que solo transicionan
|
|
91
|
+
estado deben empezar a registrar sus eventos.
|
|
92
|
+
- **Migración**: cada feature con eventos debe ajustarse para publicar los eventos del
|
|
93
|
+
agregado, aunque pueden migrarse independientemente.
|
|
94
|
+
- **Disciplina de entrega**: resolver la atomicidad entre persistir y publicar es trabajo
|
|
95
|
+
del driver; sin ello, una caída entre ambos pasos pierde eventos.
|