aw-spec-cli 0.1.4 → 0.1.6
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/assets/.agents/agent.json +28 -0
- package/assets/.agents/skills/appweaver/SKILL.md +56 -0
- package/assets/.agents/skills/appweaver-demo/SKILL.md +59 -0
- package/assets/.claude/agent.json +27 -0
- package/assets/.claude/skills/appweaver/SKILL.md +55 -0
- package/assets/.claude/skills/appweaver-demo/SKILL.md +59 -0
- package/assets/.kilocode/agent.json +28 -0
- package/assets/.kilocode/workflows/appweaver-demo.md +59 -0
- package/assets/.kilocode/workflows/appweaver.md +55 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +7 -54
- package/dist/commands/init.js.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/assets/.agents/skills/aw-spec-addcomponent/SKILL.md +0 -129
- package/assets/.agents/skills/aw-spec-agent-context-update/SKILL.md +0 -31
- package/assets/.agents/skills/aw-spec-analyze/SKILL.md +0 -296
- package/assets/.agents/skills/aw-spec-checklist/SKILL.md +0 -374
- package/assets/.agents/skills/aw-spec-clarify/SKILL.md +0 -320
- package/assets/.agents/skills/aw-spec-constitution/SKILL.md +0 -194
- package/assets/.agents/skills/aw-spec-fast-implement/SKILL.md +0 -268
- package/assets/.agents/skills/aw-spec-foundation/SKILL.md +0 -358
- package/assets/.agents/skills/aw-spec-implement/SKILL.md +0 -279
- package/assets/.agents/skills/aw-spec-minimal-implementation/SKILL.md +0 -209
- package/assets/.agents/skills/aw-spec-plan/SKILL.md +0 -221
- package/assets/.agents/skills/aw-spec-select-domain/SKILL.md +0 -73
- package/assets/.agents/skills/aw-spec-specify/SKILL.md +0 -382
- package/assets/.agents/skills/aw-spec-tasks/SKILL.md +0 -259
- package/assets/.agents/skills/aw-spec-taskstoissues/SKILL.md +0 -107
- package/assets/.aw-spec/domain.json +0 -3
- package/assets/.aw-spec/extensions/.registry +0 -19
- package/assets/.aw-spec/extensions/agent-context/README.md +0 -57
- package/assets/.aw-spec/extensions/agent-context/agent-context-config.yml +0 -4
- package/assets/.aw-spec/extensions/agent-context/commands/aw-spec.agent-context.update.md +0 -26
- package/assets/.aw-spec/extensions/agent-context/extension.yml +0 -34
- package/assets/.aw-spec/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -201
- package/assets/.aw-spec/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -237
- package/assets/.aw-spec/extensions.yml +0 -23
- package/assets/.aw-spec/feature.json +0 -3
- package/assets/.aw-spec/init-options.json +0 -9
- package/assets/.aw-spec/integration.json +0 -15
- package/assets/.aw-spec/integrations/aw-spec.manifest.json +0 -17
- package/assets/.aw-spec/integrations/claude.manifest.json +0 -17
- package/assets/.aw-spec/memory/constitution.md +0 -50
- package/assets/.aw-spec/scripts/bash/check-prerequisites.sh +0 -189
- package/assets/.aw-spec/scripts/bash/common.sh +0 -582
- package/assets/.aw-spec/scripts/bash/create-new-feature.sh +0 -299
- package/assets/.aw-spec/scripts/bash/setup-plan.sh +0 -84
- package/assets/.aw-spec/scripts/bash/setup-tasks.sh +0 -91
- package/assets/.aw-spec/templates/checklist-template.md +0 -40
- package/assets/.aw-spec/templates/constitution-template.md +0 -57
- package/assets/.aw-spec/templates/plan-template.md +0 -113
- package/assets/.aw-spec/templates/spec-template.md +0 -131
- package/assets/.aw-spec/templates/tasks-template.md +0 -252
- package/assets/.aw-spec/workflows/aw-spec/workflow.yml +0 -77
- package/assets/.aw-spec/workflows/workflow-registry.json +0 -13
- package/assets/.claude/skills/aw-spec-addcomponent/SKILL.md +0 -129
- package/assets/.claude/skills/aw-spec-agent-context-update/SKILL.md +0 -31
- package/assets/.claude/skills/aw-spec-analyze/SKILL.md +0 -296
- package/assets/.claude/skills/aw-spec-checklist/SKILL.md +0 -374
- package/assets/.claude/skills/aw-spec-clarify/SKILL.md +0 -320
- package/assets/.claude/skills/aw-spec-constitution/SKILL.md +0 -194
- package/assets/.claude/skills/aw-spec-fast-implement/SKILL.md +0 -268
- package/assets/.claude/skills/aw-spec-foundation/SKILL.md +0 -358
- package/assets/.claude/skills/aw-spec-implement/SKILL.md +0 -279
- package/assets/.claude/skills/aw-spec-minimal-implementation/SKILL.md +0 -209
- package/assets/.claude/skills/aw-spec-plan/SKILL.md +0 -221
- package/assets/.claude/skills/aw-spec-select-domain/SKILL.md +0 -73
- package/assets/.claude/skills/aw-spec-specify/SKILL.md +0 -382
- package/assets/.claude/skills/aw-spec-tasks/SKILL.md +0 -259
- package/assets/.claude/skills/aw-spec-taskstoissues/SKILL.md +0 -107
- package/assets/.kilocode/rules/specify-rules.md +0 -4
- package/assets/.kilocode/workflows/aw-spec-addcomponent.md +0 -129
- package/assets/.kilocode/workflows/aw-spec-agent-context.update.md +0 -31
- package/assets/.kilocode/workflows/aw-spec-analyze.md +0 -296
- package/assets/.kilocode/workflows/aw-spec-checklist.md +0 -374
- package/assets/.kilocode/workflows/aw-spec-clarify.md +0 -320
- package/assets/.kilocode/workflows/aw-spec-constitution.md +0 -194
- package/assets/.kilocode/workflows/aw-spec-fast-implement.md +0 -263
- package/assets/.kilocode/workflows/aw-spec-foundation.md +0 -358
- package/assets/.kilocode/workflows/aw-spec-implement.md +0 -279
- package/assets/.kilocode/workflows/aw-spec-minimal-implementation.md +0 -204
- package/assets/.kilocode/workflows/aw-spec-plan.md +0 -221
- package/assets/.kilocode/workflows/aw-spec-select-domain.md +0 -73
- package/assets/.kilocode/workflows/aw-spec-specify.md +0 -382
- package/assets/.kilocode/workflows/aw-spec-tasks.md +0 -259
- package/assets/.kilocode/workflows/aw-spec-taskstoissues.md +0 -107
- package/assets/docs/ModularMonolith/ModularMonolith.Reference.pack.txt +0 -25248
- package/assets/docs/ModularMonolith/ModularMonolith.pack.txt +0 -5726
- package/assets/docs/ModularMonolith/pack-foundation.ps1 +0 -145
- package/assets/docs/ModularMonolith/pack-foundation.sh +0 -196
- package/assets/docs/ModularMonolith/pack-reference-foundation.ps1 +0 -157
- package/assets/docs/ModularMonolith/pack-reference-foundation.sh +0 -209
- package/assets/docs/ModularMonolith/unpack-foundation.ps1 +0 -126
- package/assets/docs/ModularMonolith/unpack-foundation.sh +0 -217
- package/assets/docs/ModularMonolith/unpack-reference-foundation.ps1 +0 -129
- package/assets/docs/ModularMonolith/unpack-reference-foundation.sh +0 -218
- package/assets/docs/README.md +0 -106
- package/assets/docs/UI.Visualization/PACKAGING.md +0 -191
- package/assets/docs/UI.Visualization/UI.Visualization.Reference.pack.txt +0 -11768
- package/assets/docs/UI.Visualization/UI.Visualization.pack.txt +0 -11214
- package/assets/docs/UI.Visualization/pack-foundation.ps1 +0 -165
- package/assets/docs/UI.Visualization/pack-foundation.sh +0 -206
- package/assets/docs/UI.Visualization/pack-reference-foundation.ps1 +0 -159
- package/assets/docs/UI.Visualization/pack-reference-foundation.sh +0 -205
- package/assets/docs/UI.Visualization/unpack-foundation.ps1 +0 -126
- package/assets/docs/UI.Visualization/unpack-foundation.sh +0 -217
- package/assets/docs/UI.Visualization/unpack-reference-foundation.ps1 +0 -129
- package/assets/docs/UI.Visualization/unpack-reference-foundation.sh +0 -218
- package/assets/docs/VerticalSlice/PACKAGING.md +0 -185
- package/assets/docs/VerticalSlice/VerticalSlice.Reference.pack.txt +0 -19318
- package/assets/docs/VerticalSlice/VerticalSlice.pack.txt +0 -2833
- package/assets/docs/VerticalSlice/pack-foundation.ps1 +0 -145
- package/assets/docs/VerticalSlice/pack-foundation.sh +0 -196
- package/assets/docs/VerticalSlice/pack-reference-foundation.ps1 +0 -157
- package/assets/docs/VerticalSlice/pack-reference-foundation.sh +0 -209
- package/assets/docs/VerticalSlice/unpack-foundation.ps1 +0 -126
- package/assets/docs/VerticalSlice/unpack-foundation.sh +0 -217
- package/assets/docs/VerticalSlice/unpack-reference-foundation.ps1 +0 -129
- package/assets/docs/VerticalSlice/unpack-reference-foundation.sh +0 -218
- package/assets/docs/architecture/clean-architecture.md +0 -71
- package/assets/docs/architecture/cqrs.md +0 -141
- package/assets/docs/architecture/eventing.md +0 -83
- package/assets/docs/architecture/folder-structure.md +0 -61
- package/assets/docs/architecture/modular-monolith.md +0 -47
- package/assets/docs/architecture/persistence.md +0 -269
- package/assets/docs/architecture/testing.md +0 -55
- package/assets/docs/architecture/vertical-slices.md +0 -201
- package/assets/docs/command.txt +0 -295
- package/assets/docs/components/Email/README.md +0 -119
- package/assets/docs/components/Email/ReferenceEndpoints/SendEmailEndpoint.cs +0 -84
- package/assets/docs/components/Email/integration.md +0 -300
- package/assets/docs/components/Jobs/README.md +0 -113
- package/assets/docs/components/Jobs/integration.md +0 -259
- package/assets/docs/components/QrGenerator/README.md +0 -85
- package/assets/docs/components/QrGenerator/ReferenceEndpoints/GenerateQrEndpoint.cs +0 -57
- package/assets/docs/components/QrGenerator/integration.md +0 -230
- package/assets/docs/components/README.md +0 -74
- package/assets/docs/domain/domain-driven-design.md +0 -212
- package/assets/docs/domainspecs/UIVisualization/chart-bubble.md +0 -70
- package/assets/docs/domainspecs/UIVisualization/chart-dimred.md +0 -58
- package/assets/docs/domainspecs/UIVisualization/chart-heatmap.md +0 -62
- package/assets/docs/domainspecs/UIVisualization/chart-network.md +0 -69
- package/assets/docs/domainspecs/UIVisualization/chart-parallel.md +0 -75
- package/assets/docs/domainspecs/UIVisualization/chart-scurve.md +0 -66
- package/assets/docs/domainspecs/UIVisualization/chart-survival.md +0 -69
- package/assets/docs/domainspecs/UIVisualization/chart-trellis.md +0 -68
- package/assets/docs/domainspecs/UIVisualization/chart-violin.md +0 -61
- package/assets/docs/domainspecs/UIVisualization/chart-volcano.md +0 -63
- package/assets/docs/domainspecs/UIVisualization/charting-standards.domain.md +0 -80
- package/assets/docs/domainspecs/UIVisualization/clean-architecture.domain.md +0 -87
- package/assets/docs/domainspecs/UIVisualization/coding-standards.domain.md +0 -47
- package/assets/docs/domainspecs/UIVisualization/comandrulemap.md +0 -130
- package/assets/docs/domainspecs/UIVisualization/component-contracts.domain.md +0 -67
- package/assets/docs/domainspecs/UIVisualization/data-source.domain.md +0 -68
- package/assets/docs/domainspecs/UIVisualization/domain-requirements.md +0 -110
- package/assets/docs/domainspecs/UIVisualization/folder-structure.domain.md +0 -55
- package/assets/docs/domainspecs/UIVisualization/state-management.domain.md +0 -67
- package/assets/docs/domainspecs/UIVisualization/testing.domain.md +0 -52
- package/assets/docs/domainspecs/UIVisualization/ui-viz-terms.md +0 -39
- package/assets/docs/domainspecs/healthcare/ai-rules.domain.md +0 -34
- package/assets/docs/domainspecs/healthcare/api-versioning.domain.md +0 -17
- package/assets/docs/domainspecs/healthcare/auditing.domain.md +0 -29
- package/assets/docs/domainspecs/healthcare/authorization.domain.md +0 -26
- package/assets/docs/domainspecs/healthcare/clinical-terms.md +0 -20
- package/assets/docs/domainspecs/healthcare/coding-standards.domain.md +0 -26
- package/assets/docs/domainspecs/healthcare/comandrulemap.md +0 -166
- package/assets/docs/domainspecs/healthcare/consent.md +0 -19
- package/assets/docs/domainspecs/healthcare/cqrs.domain.md +0 -12
- package/assets/docs/domainspecs/healthcare/data-classification.domain.md +0 -28
- package/assets/docs/domainspecs/healthcare/dependency-policy.domain.md +0 -11
- package/assets/docs/domainspecs/healthcare/dicom.md +0 -15
- package/assets/docs/domainspecs/healthcare/domain-driven-design.domain.md +0 -28
- package/assets/docs/domainspecs/healthcare/domain-requirements.md +0 -55
- package/assets/docs/domainspecs/healthcare/eventing.domain.md +0 -18
- package/assets/docs/domainspecs/healthcare/fhir.md +0 -36
- package/assets/docs/domainspecs/healthcare/folder-structure.domain.md +0 -23
- package/assets/docs/domainspecs/healthcare/hipaa.md +0 -34
- package/assets/docs/domainspecs/healthcare/hl7.md +0 -15
- package/assets/docs/domainspecs/healthcare/integration-terms.md +0 -21
- package/assets/docs/domainspecs/healthcare/persistence.domain.md +0 -20
- package/assets/docs/domainspecs/healthcare/terminology.md +0 -30
- package/assets/docs/domainspecs/healthcare/testing.domain.md +0 -15
- package/assets/docs/domainspecs/healthcare/vertical-slices.domain.md +0 -18
- package/assets/docs/domainspecs/healthcare/zero-trust.domain.md +0 -12
- package/assets/docs/domainspecs/pharma/ai-rules.domain.md +0 -32
- package/assets/docs/domainspecs/pharma/api-versioning.domain.md +0 -20
- package/assets/docs/domainspecs/pharma/atc.md +0 -22
- package/assets/docs/domainspecs/pharma/auditing.domain.md +0 -26
- package/assets/docs/domainspecs/pharma/authorization.domain.md +0 -25
- package/assets/docs/domainspecs/pharma/coding-standards.domain.md +0 -28
- package/assets/docs/domainspecs/pharma/comandrulemap.md +0 -186
- package/assets/docs/domainspecs/pharma/cqrs.domain.md +0 -16
- package/assets/docs/domainspecs/pharma/data-classification.domain.md +0 -24
- package/assets/docs/domainspecs/pharma/data-provenance.md +0 -21
- package/assets/docs/domainspecs/pharma/dependency-policy.domain.md +0 -11
- package/assets/docs/domainspecs/pharma/domain-driven-design.domain.md +0 -36
- package/assets/docs/domainspecs/pharma/domain-requirements.md +0 -99
- package/assets/docs/domainspecs/pharma/eventing.domain.md +0 -18
- package/assets/docs/domainspecs/pharma/fhir.md +0 -31
- package/assets/docs/domainspecs/pharma/folder-structure.domain.md +0 -24
- package/assets/docs/domainspecs/pharma/hl7.md +0 -17
- package/assets/docs/domainspecs/pharma/idmp.md +0 -26
- package/assets/docs/domainspecs/pharma/integration-terms.md +0 -21
- package/assets/docs/domainspecs/pharma/ndc.md +0 -28
- package/assets/docs/domainspecs/pharma/persistence.domain.md +0 -23
- package/assets/docs/domainspecs/pharma/pharma-terms.md +0 -27
- package/assets/docs/domainspecs/pharma/product-lifecycle.domain.md +0 -30
- package/assets/docs/domainspecs/pharma/regulatory-compliance.md +0 -19
- package/assets/docs/domainspecs/pharma/terminology.md +0 -32
- package/assets/docs/domainspecs/pharma/testing.domain.md +0 -19
- package/assets/docs/domainspecs/pharma/vertical-slices.domain.md +0 -17
- package/assets/docs/domainspecs/pharma/zero-trust.domain.md +0 -12
- package/assets/docs/glossary/ai-rules.md +0 -59
- package/assets/docs/governance/api-versioning.md +0 -99
- package/assets/docs/governance/coding-standards.md +0 -87
- package/assets/docs/governance/dependency-policy.md +0 -40
- package/assets/docs/interop/ihe-conformance.md +0 -31
- package/assets/docs/security/auditing.md +0 -117
- package/assets/docs/security/authorization-matrix.md +0 -14
- package/assets/docs/security/authorization.md +0 -87
- package/assets/docs/security/data-classification.md +0 -38
- package/assets/docs/security/zero-trust.md +0 -76
- package/assets/docs/technology-constitution.md +0 -172
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# unpack-reference-foundation.sh — bash/macOS port of unpack-reference-foundation.ps1
|
|
4
|
-
#
|
|
5
|
-
# SYNOPSIS
|
|
6
|
-
# Unpack an AW-Spec reference pack file into a target project, renaming the
|
|
7
|
-
# sentinel app name to the caller's app name.
|
|
8
|
-
#
|
|
9
|
-
# DESCRIPTION
|
|
10
|
-
# Reads --pack-file (produced by pack-reference-foundation.sh/.ps1), takes the sentinel
|
|
11
|
-
# from its header (override with --sentinel), derives four case forms of the target app
|
|
12
|
-
# name, and writes every file block to --out-dir with the sentinel rewritten in both file
|
|
13
|
-
# CONTENTS and file/dir PATHS. Text blocks are rewritten and written as UTF-8; base64
|
|
14
|
-
# blocks are decoded as-is. Refuses to overwrite existing files unless --force.
|
|
15
|
-
#
|
|
16
|
-
# EXAMPLE
|
|
17
|
-
# ./unpack-reference-foundation.sh \
|
|
18
|
-
# --pack-file ./VerticalSlice.Reference.pack.txt \
|
|
19
|
-
# --target-app-name AppWeaver.Pharma \
|
|
20
|
-
# --out-dir ../../
|
|
21
|
-
#
|
|
22
|
-
set -euo pipefail
|
|
23
|
-
|
|
24
|
-
# ------------------------------------------------------------------ defaults ---
|
|
25
|
-
PACK_FILE=''
|
|
26
|
-
TARGET_APP_NAME=''
|
|
27
|
-
OUT_DIR='.'
|
|
28
|
-
SENTINEL=''
|
|
29
|
-
FORCE=0
|
|
30
|
-
DRYRUN=0
|
|
31
|
-
|
|
32
|
-
usage() {
|
|
33
|
-
cat >&2 <<'EOF'
|
|
34
|
-
Usage: unpack-reference-foundation.sh --pack-file <file> --target-app-name <name>
|
|
35
|
-
[--out-dir <dir>] [--sentinel <name>]
|
|
36
|
-
[--force] [--dry-run]
|
|
37
|
-
|
|
38
|
-
-p, --pack-file <file> Reference pack file to unpack (required)
|
|
39
|
-
-t, --target-app-name <name> New app name to rename the sentinel into (required)
|
|
40
|
-
-o, --out-dir <dir> Output root (default: .)
|
|
41
|
-
--sentinel <name> Override the sentinel from the pack header
|
|
42
|
-
--force Overwrite files that already exist
|
|
43
|
-
--dry-run List what would be written, write nothing
|
|
44
|
-
-h, --help Show this help
|
|
45
|
-
|
|
46
|
-
Positional fallback: unpack-reference-foundation.sh <pack-file> <target-app-name> [out-dir]
|
|
47
|
-
EOF
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
# -------------------------------------------------------------- arg parsing ---
|
|
51
|
-
while [ $# -gt 0 ]; do
|
|
52
|
-
case "$1" in
|
|
53
|
-
-p|--pack-file) PACK_FILE="${2:?--pack-file needs a value}"; shift 2 ;;
|
|
54
|
-
-t|--target-app-name) TARGET_APP_NAME="${2:?--target-app-name needs a value}"; shift 2 ;;
|
|
55
|
-
-o|--out-dir) OUT_DIR="${2:?--out-dir needs a value}"; shift 2 ;;
|
|
56
|
-
--sentinel) SENTINEL="${2:?--sentinel needs a value}"; shift 2 ;;
|
|
57
|
-
--force) FORCE=1; shift ;;
|
|
58
|
-
--dry-run) DRYRUN=1; shift ;;
|
|
59
|
-
-h|--help) usage; exit 0 ;;
|
|
60
|
-
--) shift; break ;;
|
|
61
|
-
-*) echo "Error: unknown option: $1" >&2; usage; exit 2 ;;
|
|
62
|
-
*)
|
|
63
|
-
if [ -z "$PACK_FILE" ]; then PACK_FILE="$1"
|
|
64
|
-
elif [ -z "$TARGET_APP_NAME" ]; then TARGET_APP_NAME="$1"
|
|
65
|
-
else OUT_DIR="$1"
|
|
66
|
-
fi
|
|
67
|
-
shift ;;
|
|
68
|
-
esac
|
|
69
|
-
done
|
|
70
|
-
|
|
71
|
-
[ -n "$PACK_FILE" ] || { echo "Error: --pack-file is required." >&2; usage; exit 2; }
|
|
72
|
-
[ -n "$TARGET_APP_NAME" ] || { echo "Error: --target-app-name is required." >&2; usage; exit 2; }
|
|
73
|
-
[ -f "$PACK_FILE" ] || { echo "Error: pack file not found: $PACK_FILE" >&2; exit 1; }
|
|
74
|
-
|
|
75
|
-
case "$TARGET_APP_NAME" in
|
|
76
|
-
*[!A-Za-z0-9._-]*)
|
|
77
|
-
echo "Error: --target-app-name may contain only letters, digits, '.', '_' or '-': $TARGET_APP_NAME" >&2
|
|
78
|
-
exit 2 ;;
|
|
79
|
-
esac
|
|
80
|
-
|
|
81
|
-
# ------------------------------------------------ sentinel from header --------
|
|
82
|
-
if [ -z "$SENTINEL" ]; then
|
|
83
|
-
SENTINEL="$(LC_ALL=C sed -n 's/^#[[:space:]]*sentinel:[[:space:]]*\(.*[^[:space:]]\)[[:space:]]*$/\1/p' "$PACK_FILE" | head -n1)"
|
|
84
|
-
fi
|
|
85
|
-
[ -n "$SENTINEL" ] || { echo "Error: Sentinel not found in pack header; supply it with --sentinel." >&2; exit 1; }
|
|
86
|
-
|
|
87
|
-
# ------------------------------------------------ derive case forms -----------
|
|
88
|
-
to_lower() { printf '%s' "$1" | LC_ALL=C tr '[:upper:]' '[:lower:]'; }
|
|
89
|
-
|
|
90
|
-
S_DOT="$SENTINEL"; T_DOT="$TARGET_APP_NAME"
|
|
91
|
-
S_UND="${SENTINEL//./_}"; T_UND="${TARGET_APP_NAME//./_}"
|
|
92
|
-
s_low="$(to_lower "$SENTINEL")"; t_low="$(to_lower "$TARGET_APP_NAME")"
|
|
93
|
-
S_HYP="${s_low//./-}"; T_HYP="${t_low//./-}"
|
|
94
|
-
S_LDOT="$s_low"; T_LDOT="$t_low"
|
|
95
|
-
|
|
96
|
-
FROM=(); TO=()
|
|
97
|
-
add_form() {
|
|
98
|
-
local from="$1" to="$2" e
|
|
99
|
-
[ -n "$from" ] || return 0
|
|
100
|
-
[ "$from" = "$to" ] && return 0
|
|
101
|
-
if [ "${#FROM[@]}" -gt 0 ]; then
|
|
102
|
-
for e in "${FROM[@]}"; do [ "$e" = "$from" ] && return 0; done
|
|
103
|
-
fi
|
|
104
|
-
FROM+=("$from"); TO+=("$to")
|
|
105
|
-
}
|
|
106
|
-
add_form "$S_DOT" "$T_DOT"
|
|
107
|
-
add_form "$S_UND" "$T_UND"
|
|
108
|
-
add_form "$S_HYP" "$T_HYP"
|
|
109
|
-
add_form "$S_LDOT" "$T_LDOT"
|
|
110
|
-
|
|
111
|
-
# Sort indices by FROM length descending
|
|
112
|
-
ORDER=()
|
|
113
|
-
{
|
|
114
|
-
local_n="${#FROM[@]}"
|
|
115
|
-
used=(); for ((k = 0; k < local_n; k++)); do used[$k]=0; done
|
|
116
|
-
for ((pass = 0; pass < local_n; pass++)); do
|
|
117
|
-
best=-1; bestlen=-1
|
|
118
|
-
for ((j = 0; j < local_n; j++)); do
|
|
119
|
-
if [ "${used[$j]}" -eq 0 ] && [ "${#FROM[$j]}" -gt "$bestlen" ]; then
|
|
120
|
-
bestlen="${#FROM[$j]}"; best="$j"
|
|
121
|
-
fi
|
|
122
|
-
done
|
|
123
|
-
used[$best]=1; ORDER+=("$best")
|
|
124
|
-
done
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
REPL_OUT=''
|
|
128
|
-
apply_repl() {
|
|
129
|
-
local text="$1" idx from to
|
|
130
|
-
if [ "${#ORDER[@]}" -gt 0 ]; then
|
|
131
|
-
for idx in "${ORDER[@]}"; do
|
|
132
|
-
from="${FROM[$idx]}"; to="${TO[$idx]}"
|
|
133
|
-
text="${text//"$from"/$to}"
|
|
134
|
-
done
|
|
135
|
-
fi
|
|
136
|
-
REPL_OUT="$text"
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
# ------------------------------------------------ output root ----------------
|
|
140
|
-
[ -d "$OUT_DIR" ] || mkdir -p "$OUT_DIR"
|
|
141
|
-
OUT_ROOT="$(cd "$OUT_DIR" && pwd -P)"
|
|
142
|
-
|
|
143
|
-
# ------------------------------------------------ base64 decoder (portable) --
|
|
144
|
-
B64D=()
|
|
145
|
-
if printf 'aGk=' | base64 -d >/dev/null 2>&1; then B64D=(base64 -d)
|
|
146
|
-
elif printf 'aGk=' | base64 -D >/dev/null 2>&1; then B64D=(base64 -D)
|
|
147
|
-
elif printf 'aGk=' | base64 --decode >/dev/null 2>&1; then B64D=(base64 --decode)
|
|
148
|
-
else B64D=(openssl base64 -d -A)
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
# ------------------------------------------------ parse file blocks ----------
|
|
152
|
-
rels=(); encs=(); contents=()
|
|
153
|
-
in_block=0; cur_rel=''; cur_enc=''; cur_content=''; first_line=1
|
|
154
|
-
file_re='^<<<FILE path="([^"]+)"( encoding="([^"]+)")?>>>[[:space:]]*$'
|
|
155
|
-
|
|
156
|
-
while IFS= read -r line || [ -n "$line" ]; do
|
|
157
|
-
line="${line%$'\r'}"
|
|
158
|
-
if [ "$in_block" -eq 0 ]; then
|
|
159
|
-
if [[ "$line" =~ $file_re ]]; then
|
|
160
|
-
cur_rel="${BASH_REMATCH[1]}"
|
|
161
|
-
cur_enc="${BASH_REMATCH[3]:-}"
|
|
162
|
-
in_block=1; first_line=1; cur_content=''
|
|
163
|
-
fi
|
|
164
|
-
else
|
|
165
|
-
if [ "$line" = '<<<ENDFILE>>>' ]; then
|
|
166
|
-
rels+=("$cur_rel"); encs+=("$cur_enc"); contents+=("$cur_content")
|
|
167
|
-
in_block=0
|
|
168
|
-
elif [ "$first_line" -eq 1 ]; then
|
|
169
|
-
cur_content="$line"; first_line=0
|
|
170
|
-
else
|
|
171
|
-
cur_content="$cur_content"$'\n'"$line"
|
|
172
|
-
fi
|
|
173
|
-
fi
|
|
174
|
-
done < "$PACK_FILE"
|
|
175
|
-
|
|
176
|
-
[ "${#rels[@]}" -gt 0 ] || { echo "Error: No <<<FILE>>> blocks found in pack: $PACK_FILE" >&2; exit 1; }
|
|
177
|
-
|
|
178
|
-
# ------------------------------------------------ resolve destinations -------
|
|
179
|
-
dests=()
|
|
180
|
-
for ((i = 0; i < ${#rels[@]}; i++)); do
|
|
181
|
-
apply_repl "${rels[$i]}"
|
|
182
|
-
dests+=("$OUT_ROOT/$REPL_OUT")
|
|
183
|
-
done
|
|
184
|
-
|
|
185
|
-
# ------------------------------------------------ conflict guard -------------
|
|
186
|
-
if [ "$FORCE" -eq 0 ] && [ "$DRYRUN" -eq 0 ]; then
|
|
187
|
-
conflicts=()
|
|
188
|
-
for d in "${dests[@]}"; do [ -e "$d" ] && conflicts+=("$d"); done
|
|
189
|
-
if [ "${#conflicts[@]}" -gt 0 ]; then
|
|
190
|
-
echo "Error: Refusing to overwrite existing files (re-run with --force):" >&2
|
|
191
|
-
printf ' %s\n' "${conflicts[@]}" >&2
|
|
192
|
-
exit 1
|
|
193
|
-
fi
|
|
194
|
-
fi
|
|
195
|
-
|
|
196
|
-
# ------------------------------------------------ write ----------------------
|
|
197
|
-
written=0
|
|
198
|
-
for ((i = 0; i < ${#rels[@]}; i++)); do
|
|
199
|
-
rel="${rels[$i]}"; enc="${encs[$i]}"; dest="${dests[$i]}"
|
|
200
|
-
if [ "$DRYRUN" -eq 1 ]; then
|
|
201
|
-
echo "WOULD WRITE $rel -> $dest"
|
|
202
|
-
continue
|
|
203
|
-
fi
|
|
204
|
-
mkdir -p "$(dirname "$dest")"
|
|
205
|
-
if [ "$enc" = 'base64' ]; then
|
|
206
|
-
printf '%s' "${contents[$i]}" | tr -d '[:space:]' | "${B64D[@]}" > "$dest"
|
|
207
|
-
else
|
|
208
|
-
apply_repl "${contents[$i]}"
|
|
209
|
-
printf '%s' "$REPL_OUT" > "$dest"
|
|
210
|
-
fi
|
|
211
|
-
written=$((written + 1))
|
|
212
|
-
done
|
|
213
|
-
|
|
214
|
-
if [ "$DRYRUN" -eq 1 ]; then
|
|
215
|
-
echo "DryRun: ${#rels[@]} file(s) would be written to $OUT_ROOT (sentinel '$SENTINEL' -> '$TARGET_APP_NAME')."
|
|
216
|
-
else
|
|
217
|
-
echo "Unpacked $written file(s) to $OUT_ROOT (sentinel '$SENTINEL' -> '$TARGET_APP_NAME')."
|
|
218
|
-
fi
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# Clean Architecture Standards
|
|
2
|
-
|
|
3
|
-
> Pure technology (domain-agnostic). NON-NEGOTIABLE. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
-
|
|
5
|
-
## Dependency direction
|
|
6
|
-
|
|
7
|
-
Dependencies flow **inward only**:
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
API -> Application (Features) -> Domain <- Infrastructure
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
The Domain is the innermost ring; everything depends on it and it depends on nothing.
|
|
14
|
-
|
|
15
|
-
## Layer responsibilities
|
|
16
|
-
|
|
17
|
-
- **API** — HTTP surface (FastEndpoints). Translates transport to/from mediator
|
|
18
|
-
messages. MUST NOT contain business logic.
|
|
19
|
-
- **Application (Features)** — Command/Query handlers, validators, orchestration.
|
|
20
|
-
Dispatched via `IMediator`. MUST NOT depend on API frameworks or concrete EF Core types.
|
|
21
|
-
- **Domain** — aggregates, entities, value objects, domain events, specifications.
|
|
22
|
-
Framework-agnostic.
|
|
23
|
-
- **Infrastructure** — EF Core, external service clients, persistence configuration.
|
|
24
|
-
Depends inward on Domain abstractions.
|
|
25
|
-
|
|
26
|
-
## Allowed dependencies / annotations in Domain
|
|
27
|
-
|
|
28
|
-
The Domain layer is framework-agnostic. The **only** allowed annotations are:
|
|
29
|
-
|
|
30
|
-
- `[NotMapped]`
|
|
31
|
-
- `[ValueObject<T>]` (Vogen)
|
|
32
|
-
- `System.ComponentModel.DataAnnotations.Schema`
|
|
33
|
-
|
|
34
|
-
## Infrastructure boundaries
|
|
35
|
-
|
|
36
|
-
- EF Core mapping lives **only** in `{Module}/Persistence/<Aggregate>Configuration.cs` and is
|
|
37
|
-
applied via `ApplyConfigurationsFromAssembly`.
|
|
38
|
-
- **Endpoints MUST NOT** call repositories, `DbContext`, or `IUnitOfWork` directly; they
|
|
39
|
-
**MUST** call `IMediator.Send(...)`.
|
|
40
|
-
- **Application Features MUST NOT** depend on API frameworks or concrete EF Core types.
|
|
41
|
-
- Direct `DbContext` access from Endpoints or Application Features (outside the
|
|
42
|
-
repository / specification surface) is FORBIDDEN.
|
|
43
|
-
|
|
44
|
-
## Canonical example (agent reference — `<SampleProjectName>` sample)
|
|
45
|
-
|
|
46
|
-
The dependency rule in practice — the **API** layer depends inward via `IMediator` only
|
|
47
|
-
(never `IRepository`/`DbContext`); the **Application** layer depends on Domain abstractions
|
|
48
|
-
(`IRepository<Project>`), never on concrete EF Core types:
|
|
49
|
-
|
|
50
|
-
```csharp
|
|
51
|
-
// API layer — <SampleProjectName>.Service/Projects/Features/CreateProject/CreateProjectEndpoint.cs
|
|
52
|
-
public class CreateProjectEndpoint(IMediator _mediator)
|
|
53
|
-
: SecureFastEndpoint<CreateProjectRequest, ProjectDto>
|
|
54
|
-
{
|
|
55
|
-
protected override Task<Result<ProjectDto>> ExecuteAsync(
|
|
56
|
-
CreateProjectRequest req, CancellationToken ct)
|
|
57
|
-
=> _mediator.Send(new CreateProjectCommand(
|
|
58
|
-
req.Code, req.Name, req.Description, req.StartDate, req.EndDate, req.OwnerId), ct);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Application layer — depends on Domain abstractions, not EF Core
|
|
62
|
-
public class CreateProjectCommandHandler(
|
|
63
|
-
IRepository<Project> _repo, IUnitOfWork _uow, IPublisher _eventBus)
|
|
64
|
-
: ICommandHandler<CreateProjectCommand, Result<ProjectDto>> { /* ... */ }
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Related
|
|
68
|
-
|
|
69
|
-
- [[cqrs]] — how the Application layer dispatches work
|
|
70
|
-
- [[persistence]] — repository + specification surface, EF Core conventions
|
|
71
|
-
- [[vertical-slices]] — physical organization of the layers per feature
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
# CQRS + Mediator Standards
|
|
2
|
-
|
|
3
|
-
> Pure technology (domain-agnostic) — domain additions in [[cqrs.domain]]. NON-NEGOTIABLE. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
-
|
|
5
|
-
## Command / query patterns
|
|
6
|
-
|
|
7
|
-
- All application logic is dispatched through **`AppWeaver.Mediator`** (NOT MediatR).
|
|
8
|
-
- **Commands:** `record XxxCommand(...) : ICommand<Result<TDto>>` or `ICommand<Result>`.
|
|
9
|
-
- **Queries:** `record XxxQuery(...) : IQuery<Result<TDto>>`.
|
|
10
|
-
- Handlers use **primary-constructor DI** and return `ValueTask<Result<T>>`.
|
|
11
|
-
|
|
12
|
-
## Mediator conventions
|
|
13
|
-
|
|
14
|
-
- Endpoints call `IMediator.Send(...)` — never repositories/`DbContext`/`IUnitOfWork` directly.
|
|
15
|
-
- Raw `Guid` parameters at the Application boundary **MUST be wrapped** into strongly-typed
|
|
16
|
-
IDs at the handler edge.
|
|
17
|
-
|
|
18
|
-
## Pipeline behaviors
|
|
19
|
-
|
|
20
|
-
Enabled at startup, in order:
|
|
21
|
-
|
|
22
|
-
1. Logging behavior
|
|
23
|
-
2. FluentValidation behavior
|
|
24
|
-
|
|
25
|
-
> Additional cross-cutting behaviors (e.g. domain access-audit behaviors) may be appended —
|
|
26
|
-
> see [[cqrs.domain]] for the domain audit-pipeline behavior.
|
|
27
|
-
|
|
28
|
-
## Result pattern
|
|
29
|
-
|
|
30
|
-
- Errors are returned as `Result<T>.Failure(new Error(code, message, status, resource))`.
|
|
31
|
-
- **Exceptions MUST NOT** be used for expected business outcomes (validation, not-found,
|
|
32
|
-
conflict, concurrency). `DomainException` is reserved for invariant violations and is
|
|
33
|
-
caught by the centralized exception handler.
|
|
34
|
-
- **Error code** MUST be a stable dot-namespaced slug — e.g. `resource.not_found`,
|
|
35
|
-
`concurrency.conflict`, `resource.duplicate`.
|
|
36
|
-
- **resource** MUST be the kebab-case resource name.
|
|
37
|
-
|
|
38
|
-
## Handler responsibilities
|
|
39
|
-
|
|
40
|
-
- **Read handlers** inject `IReadRepository<TAggregate>`.
|
|
41
|
-
- **Write handlers** inject `IRepository<TAggregate>` + `IUnitOfWork` (+ `IPublisher`
|
|
42
|
-
when domain events are produced).
|
|
43
|
-
- Validators check **request shape only**; invariants are enforced inside the aggregate.
|
|
44
|
-
- No business logic in handlers (anemic-domain anti-pattern — see [[domain-driven-design]]).
|
|
45
|
-
|
|
46
|
-
## Transaction handling
|
|
47
|
-
|
|
48
|
-
- Write handlers **MUST** wrap mutating work in
|
|
49
|
-
`IUnitOfWork.ExecuteInTransactionAsync(..., IsolationLevel.ReadCommitted, ct)` and call
|
|
50
|
-
`SaveChangesAsync` **inside** the lambda.
|
|
51
|
-
- They **MUST** publish domain events and call `ClearDomainEvents()` before returning.
|
|
52
|
-
- **One command mutates one aggregate per transaction** — cross-aggregate effects flow
|
|
53
|
-
through domain events or follow-up commands (see [[eventing]]).
|
|
54
|
-
|
|
55
|
-
## Canonical example (agent reference — `<SampleProjectName>` sample)
|
|
56
|
-
|
|
57
|
-
Command as a `record`, write handler with transaction + event publication, and a read handler
|
|
58
|
-
returning `Result.Failure(...)` for the expected not-found outcome (no exceptions):
|
|
59
|
-
|
|
60
|
-
```csharp
|
|
61
|
-
// Command — record implementing ICommand<Result<TDto>>
|
|
62
|
-
public record CreateProjectCommand(
|
|
63
|
-
string Code, string Name, string? Description,
|
|
64
|
-
DateOnly StartDate, DateOnly? EndDate, Guid? OwnerId
|
|
65
|
-
) : ICommand<Result<ProjectDto>>;
|
|
66
|
-
|
|
67
|
-
// Write handler — primary-ctor DI, transaction, publish + clear events, Result<T>
|
|
68
|
-
public class CreateProjectCommandHandler(
|
|
69
|
-
IRepository<Project> _repo, IUnitOfWork _uow, IPublisher _eventBus
|
|
70
|
-
) : ICommandHandler<CreateProjectCommand, Result<ProjectDto>>
|
|
71
|
-
{
|
|
72
|
-
public async ValueTask<Result<ProjectDto>> Handle(
|
|
73
|
-
CreateProjectCommand request, CancellationToken ct)
|
|
74
|
-
=> await _uow.ExecuteInTransactionAsync(async () =>
|
|
75
|
-
{
|
|
76
|
-
var ownerId = request.OwnerId.HasValue
|
|
77
|
-
? TaskUserId.From(request.OwnerId.Value) : (TaskUserId?)null;
|
|
78
|
-
var entity = new Project(request.Code, request.Name, request.StartDate, ownerId);
|
|
79
|
-
|
|
80
|
-
await _repo.AddAsync(entity, ct);
|
|
81
|
-
await _uow.SaveChangesAsync(ct);
|
|
82
|
-
|
|
83
|
-
foreach (var e in entity.DomainEvents) await _eventBus.Publish(e, ct);
|
|
84
|
-
entity.ClearDomainEvents();
|
|
85
|
-
|
|
86
|
-
return Result<ProjectDto>.Ok(entity.ToDto());
|
|
87
|
-
}, IsolationLevel.ReadCommitted, ct);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Read handler — IReadRepository + Specification; not-found is a Result, not an exception
|
|
91
|
-
public class GetProjectByIdQueryHandler(IReadRepository<Project> _repo)
|
|
92
|
-
: IQueryHandler<GetProjectByIdQuery, Result<ProjectDto>>
|
|
93
|
-
{
|
|
94
|
-
public async ValueTask<Result<ProjectDto>> Handle(
|
|
95
|
-
GetProjectByIdQuery request, CancellationToken ct)
|
|
96
|
-
{
|
|
97
|
-
Guard.NotDefault(request.Id, nameof(request.Id));
|
|
98
|
-
var entity = await _repo.FirstOrDefaultAsync(
|
|
99
|
-
new ProjectWithTasksSpec(ProjectId.From(request.Id)), ct);
|
|
100
|
-
|
|
101
|
-
return entity is null
|
|
102
|
-
? Result<ProjectDto>.Failure(new Error(
|
|
103
|
-
"project.not_found", $"Project {request.Id} not found", 404, "projects"))
|
|
104
|
-
: Result<ProjectDto>.Ok(entity.ToDto());
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Commands with no return payload use `ICommand<Result>` (e.g. delete) and return
|
|
110
|
-
`Result.Success()` / `Result.Failure(...)`:
|
|
111
|
-
|
|
112
|
-
```csharp
|
|
113
|
-
public record DeleteProjectCommand(Guid Id, string RowVersionBase64) : ICommand<Result>;
|
|
114
|
-
|
|
115
|
-
public class DeleteProjectCommandHandler(IRepository<Project> _repo, IUnitOfWork _uow, IPublisher _bus)
|
|
116
|
-
: ICommandHandler<DeleteProjectCommand, Result> { /* ... returns Result.Success() */ }
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Composition — register the mediator with the logging + validation pipeline behaviors,
|
|
120
|
-
the validators, and the in-process event bus (one-time startup wiring):
|
|
121
|
-
|
|
122
|
-
```csharp
|
|
123
|
-
// <SampleProjectName>.Service/Extensions/ApplicationServiceRegistration.cs
|
|
124
|
-
services.AddMediator(builder =>
|
|
125
|
-
{
|
|
126
|
-
builder.UseDefault()
|
|
127
|
-
.UseBehaviors()
|
|
128
|
-
.AddLoggingBehavior()
|
|
129
|
-
.AddFluentValidationBehavior(); // domain projects also add PHI-audit — see [[cqrs.domain]]
|
|
130
|
-
}, options => options.Provider = "Default");
|
|
131
|
-
|
|
132
|
-
services.AddAppWeaverMediatorHandlersFrom(typeof(ApplicationServiceRegistration));
|
|
133
|
-
services.AddValidatorsFromAssembly(typeof(ApplicationServiceRegistration).Assembly);
|
|
134
|
-
|
|
135
|
-
services.AddEventBus(b => b.UseDefaultEventBus(o =>
|
|
136
|
-
o.RegisterServicesFromAssembly(typeof(ApplicationServiceRegistration).Assembly)));
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## Related
|
|
140
|
-
|
|
141
|
-
- [[clean-architecture]] · [[persistence]] · [[eventing]] · [[domain-driven-design]] · [[cqrs.domain]]
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# Eventing Standards
|
|
2
|
-
|
|
3
|
-
> Pure technology (domain-agnostic) — domain additions in [[eventing.domain]]. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
-
|
|
5
|
-
## Domain events
|
|
6
|
-
|
|
7
|
-
- Domain Events derive from `DomainEventBase`, are **immutable**, and are added **inside**
|
|
8
|
-
aggregate methods via `AddDomainEvent(...)`.
|
|
9
|
-
- They are published **explicitly** by command handlers: `foreach` over `entity.DomainEvents`,
|
|
10
|
-
then `ClearDomainEvents()` before returning. See [[cqrs]].
|
|
11
|
-
- Cross-aggregate effects flow through domain events or follow-up commands — never a
|
|
12
|
-
multi-aggregate transaction.
|
|
13
|
-
|
|
14
|
-
## CloudEvents conventions
|
|
15
|
-
|
|
16
|
-
- Asynchronous propagation of domain events to downstream consumers uses a **durable event
|
|
17
|
-
stream**: **CloudEvents** over the configured broker (e.g. Azure Service Bus, AWS SQS/SNS,
|
|
18
|
-
or RabbitMQ).
|
|
19
|
-
- Each module owns its **public events**; downstream consumers subscribe to them rather than
|
|
20
|
-
reading the OLTP database.
|
|
21
|
-
|
|
22
|
-
## Event immutability
|
|
23
|
-
|
|
24
|
-
- Published events are immutable facts. Consumers build their own read models; they
|
|
25
|
-
**MUST NOT** query the source OLTP database directly.
|
|
26
|
-
- Analytics / reporting is built from emitted events — **never** by querying the OLTP DB
|
|
27
|
-
directly.
|
|
28
|
-
|
|
29
|
-
## Event versioning
|
|
30
|
-
|
|
31
|
-
- The service publishes a versioned, machine-readable **AsyncAPI** document for event
|
|
32
|
-
contracts.
|
|
33
|
-
- Breaking changes to a domain-event contract are **MAJOR** version bumps and require the
|
|
34
|
-
AsyncAPI document to regenerate cleanly (CI gate). See [[api-versioning]].
|
|
35
|
-
|
|
36
|
-
## Broker integration patterns
|
|
37
|
-
|
|
38
|
-
- The message broker is declared as an Aspire resource and referenced via `.WithReference(...)`;
|
|
39
|
-
connection details are discovered through Aspire, never hard-coded.
|
|
40
|
-
- Trace context **MUST** propagate across `IMediator.Send`, `HttpClient`, and EF Core; bypassing
|
|
41
|
-
the framework's `HttpClientFactory` is forbidden.
|
|
42
|
-
(Outbound domain-protocol propagation is covered in [[eventing.domain]].)
|
|
43
|
-
|
|
44
|
-
## Inbound integration rule (generic)
|
|
45
|
-
|
|
46
|
-
- Inbound integrations flow through **Interop adapters** that translate to internal commands;
|
|
47
|
-
they **MUST NOT** write directly to aggregates. (Domain feed types in [[eventing.domain]].)
|
|
48
|
-
|
|
49
|
-
## Canonical example (agent reference — `<SampleProjectName>` sample)
|
|
50
|
-
|
|
51
|
-
Immutable domain event, raised inside the aggregate, published explicitly by the handler:
|
|
52
|
-
|
|
53
|
-
```csharp
|
|
54
|
-
// <SampleProjectName>.Service/Projects/Domain/Events/ProjectCreatedEvent.cs
|
|
55
|
-
public class ProjectCreatedEvent(Guid projectId, string code, string name) : DomainEventBase
|
|
56
|
-
{
|
|
57
|
-
public Guid ProjectId { get; } = projectId; // get-only — immutable
|
|
58
|
-
public string Code { get; } = code;
|
|
59
|
-
public string Name { get; } = name;
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
```csharp
|
|
64
|
-
// Raised inside the aggregate method (never in the handler):
|
|
65
|
-
AddDomainEvent(new ProjectCreatedEvent(Id.Value, Code.Value, Name));
|
|
66
|
-
|
|
67
|
-
// Published by the command handler after SaveChanges, then cleared:
|
|
68
|
-
foreach (var domainEvent in entity.DomainEvents)
|
|
69
|
-
await _eventBus.Publish(domainEvent, ct); // IPublisher — in-process today, broker tomorrow
|
|
70
|
-
entity.ClearDomainEvents();
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Composition — register the event bus (handlers discovered from the assembly):
|
|
74
|
-
|
|
75
|
-
```csharp
|
|
76
|
-
// <SampleProjectName>.Service/Extensions/ApplicationServiceRegistration.cs
|
|
77
|
-
services.AddEventBus(b => b.UseDefaultEventBus(o =>
|
|
78
|
-
o.RegisterServicesFromAssembly(typeof(ApplicationServiceRegistration).Assembly)));
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Related
|
|
82
|
-
|
|
83
|
-
- [[cqrs]] · [[modular-monolith]] · [[api-versioning]] · [[eventing.domain]]
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# Mandatory Folder Structure
|
|
2
|
-
|
|
3
|
-
> Pure technology (domain-agnostic) — domain additions (Interop adapters, conformance tests, fixtures) in [[folder-structure.domain]]. NON-NEGOTIABLE. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
-
>
|
|
5
|
-
> Replace `<SampleProjectName>` with the project name (e.g. `Acme.Billing`).
|
|
6
|
-
|
|
7
|
-
## Layout
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
src/
|
|
11
|
-
<SampleProjectName>.AppHost/ Aspire composition root (Program.cs)
|
|
12
|
-
<SampleProjectName>.ServiceDefaults/ OTel + Serilog + resilience + service discovery
|
|
13
|
-
<SampleProjectName>.Service/ headless API runtime (a.k.a. <SampleProjectName>.Api)
|
|
14
|
-
Program.cs composition only
|
|
15
|
-
GlobalUsings.cs
|
|
16
|
-
appsettings.json / appsettings.{Env}.json
|
|
17
|
-
Common/ PagedResult<T> + shared value objects
|
|
18
|
-
Extensions/ ApplicationServiceRegistration (mediator/eventbus),
|
|
19
|
-
InfrastructureServiceRegistration (DbContext/repos),
|
|
20
|
-
Aspire/Host extensions, SpecificationExtensions
|
|
21
|
-
Infrastructure/
|
|
22
|
-
ApplicationDbContext.cs ContextAwareDbContext<T>
|
|
23
|
-
*DbContextFactory.cs design-time factories
|
|
24
|
-
Migrations/
|
|
25
|
-
Utilities/ ETag/HTTP helpers (HttpRequest/Response ETag ext)
|
|
26
|
-
<Module>/ one folder per bounded context
|
|
27
|
-
Domain/
|
|
28
|
-
<Aggregate>.cs
|
|
29
|
-
<ChildEntity>.cs
|
|
30
|
-
<ValueObject>.cs
|
|
31
|
-
<Aggregate>Id.cs [ValueObject<Guid>] partial struct
|
|
32
|
-
Enums/ (optional) status/priority enums
|
|
33
|
-
Events/ DomainEventBase descendants
|
|
34
|
-
Specifications/ Specification<TAggregate>
|
|
35
|
-
Persistence/
|
|
36
|
-
<Aggregate>Configuration.cs (+ <ChildEntity>Configuration.cs)
|
|
37
|
-
Features/<FeatureName>/ Command/Query + Handler + Endpoint + Validator
|
|
38
|
-
Shared/ module-public DTOs + ToDto mapping extensions
|
|
39
|
-
tests/
|
|
40
|
-
<SampleProjectName>.UnitTests/
|
|
41
|
-
<SampleProjectName>.IntegrationTests/ Testcontainers-driven
|
|
42
|
-
<SampleProjectName>.TestFixtures/ shared test fixtures
|
|
43
|
-
helm/
|
|
44
|
-
<SampleProjectName>.Api/
|
|
45
|
-
docs/ engineering standards (see [[README]] + context map)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Rules
|
|
49
|
-
|
|
50
|
-
- Each **module** is a top-level folder with `Domain / Features / Persistence / Shared`
|
|
51
|
-
(see [[modular-monolith]], [[vertical-slices]]).
|
|
52
|
-
- Each **feature** is a self-contained vertical slice (Command/Query + Handler + Endpoint +
|
|
53
|
-
Validator co-located).
|
|
54
|
-
- Cross-cutting files (`Program.cs`, `GlobalUsings.cs`, `Directory.Build.props`,
|
|
55
|
-
`Directory.Packages.props`, `global.json`) MUST stay at the level they occupy in the
|
|
56
|
-
AppWeaver template.
|
|
57
|
-
- New folders outside this structure require a constitution amendment.
|
|
58
|
-
|
|
59
|
-
## Related
|
|
60
|
-
|
|
61
|
-
- [[modular-monolith]] · [[vertical-slices]] · [[clean-architecture]] · [[folder-structure.domain]]
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Modular Monolith Standards
|
|
2
|
-
|
|
3
|
-
> Pure technology (domain-agnostic). NON-NEGOTIABLE where marked. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
-
|
|
5
|
-
## Module boundaries
|
|
6
|
-
|
|
7
|
-
- The runtime is a **single deployable service** partitioned into bounded contexts (modules).
|
|
8
|
-
- Each module is a **top-level folder** containing four mandatory child folders:
|
|
9
|
-
`Domain`, `Features`, `Persistence`, `Shared` (plus `Interop` where applicable).
|
|
10
|
-
- A module **owns** its aggregates, persistence configurations, DTOs, integration
|
|
11
|
-
adapters/projectors, and public events. Nothing outside the module may reach past these.
|
|
12
|
-
|
|
13
|
-
## Cross-module communication
|
|
14
|
-
|
|
15
|
-
- Modules **MUST NOT** reference another module's `Domain` types except through
|
|
16
|
-
**strongly-typed IDs** or the other module's **`Shared` DTOs**.
|
|
17
|
-
- Modules **MUST NOT** take EF Core `DbSet` dependencies on entities they do not own.
|
|
18
|
-
- **Cross-module reads** go through the other module's `Shared` DTOs or its `IMediator`
|
|
19
|
-
queries — never through its `DbContext` entities.
|
|
20
|
-
- **Cross-module writes** go through commands published to `IMediator` (in-process today,
|
|
21
|
-
on the event bus tomorrow). A module **MUST NOT** acquire a write lock on another
|
|
22
|
-
module's aggregate.
|
|
23
|
-
- No horizontal "Services/Managers" layers across slices.
|
|
24
|
-
|
|
25
|
-
## Schema-per-module guidance
|
|
26
|
-
|
|
27
|
-
- Per-module schemas in PostgreSQL (**schema-per-module**) are RECOMMENDED so a future
|
|
28
|
-
service extraction is a clean cutover.
|
|
29
|
-
|
|
30
|
-
## Module isolation / testability
|
|
31
|
-
|
|
32
|
-
- Every module **MUST be testable in isolation**: its unit tests and handler tests
|
|
33
|
-
**MUST NOT** require any other module to be loaded.
|
|
34
|
-
|
|
35
|
-
## Future microservice extraction strategy
|
|
36
|
-
|
|
37
|
-
- Today's modules are tomorrow's services. Splitting a module into its own service
|
|
38
|
-
**MUST require zero changes to the domain code of other modules**.
|
|
39
|
-
- Migration to microservices is **approved by amendment** when a module's deployment
|
|
40
|
-
cadence, scaling profile, team ownership, or regulatory boundary materially diverges
|
|
41
|
-
from the rest of the monolith.
|
|
42
|
-
|
|
43
|
-
## Related
|
|
44
|
-
|
|
45
|
-
- [[vertical-slices]] — feature organization within a module
|
|
46
|
-
- [[clean-architecture]] — dependency rule across layers
|
|
47
|
-
- [[eventing]] — async cross-module/event-bus propagation
|