aw-spec-cli 0.1.8 → 0.1.9
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/.appweaver/DESIGN-FORMAT.md +200 -0
- package/assets/.appweaver/ORCH-DIALECT.md +156 -0
- package/assets/.appweaver/RULE-PARTITION.md +175 -0
- package/assets/.appweaver/STACK-PROFILES.md +461 -0
- package/assets/.appweaver/ai-rules.md +180 -0
- package/assets/.appweaver/domain.json +1 -0
- package/assets/.appweaver/extensions/.registry +19 -0
- package/assets/.appweaver/extensions/agent-context/README.md +57 -0
- package/assets/.appweaver/extensions/agent-context/agent-context-config.yml +4 -0
- package/assets/.appweaver/extensions/agent-context/commands/aw-spec.agent-context.update.md +26 -0
- package/assets/.appweaver/extensions/agent-context/extension.yml +37 -0
- package/assets/.appweaver/extensions/agent-context/scripts/bash/update-agent-context.sh +201 -0
- package/assets/.appweaver/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +237 -0
- package/assets/.appweaver/extensions.yml +16 -0
- package/assets/.appweaver/feature.json +3 -0
- package/assets/.appweaver/init-options.json +9 -0
- package/assets/.appweaver/integration.json +15 -0
- package/assets/.appweaver/integrations/aw-spec.manifest.json +9 -0
- package/assets/.appweaver/interpreter/run-skill.py +1176 -0
- package/assets/.appweaver/memory/constitution.md +50 -0
- package/assets/.appweaver/orchestrator/.smoke-out/smoke.md +6 -0
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +94 -0
- package/assets/.appweaver/orchestrator/README.md +300 -0
- package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +74 -0
- package/assets/.appweaver/orchestrator/adapters/google_antigravity.py +74 -0
- package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +105 -0
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +101 -0
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +90 -0
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +126 -0
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +123 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +1721 -0
- package/assets/.appweaver/orchestrator/rev-templates/ai-rules.reference.md +40 -0
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +101 -0
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +245 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md +63 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +48 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +59 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-file.skeleton.md +39 -0
- package/assets/.appweaver/orchestrator/rev-templates/frontend-rule-file.skeleton.md +63 -0
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +116 -0
- package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +106 -0
- package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +54 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-constitution.skeleton.md +125 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +123 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +294 -0
- package/assets/.appweaver/orchestrator/workflows/_smoke.orch.yaml +160 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-add-component.orch.yaml +270 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +934 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +251 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +265 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +437 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +197 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +475 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +181 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +727 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-demo.orch.yaml +100 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-dependency-tree.orch.yaml +69 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +85 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design-ui.orch.yaml +386 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design.orch.yaml +382 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +462 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +265 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +253 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +267 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +287 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +238 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +1822 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +293 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +194 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +83 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +128 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +296 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +439 -0
- package/assets/.appweaver/scripts/bash/aw-inventory.sh +17 -0
- package/assets/.appweaver/scripts/bash/aw-parity.sh +13 -0
- package/assets/.appweaver/scripts/bash/aw-rev-constitution.sh +13 -0
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +2626 -0
- package/assets/.appweaver/scripts/bash/aw-xref.sh +15 -0
- package/assets/.appweaver/scripts/bash/common.sh +533 -0
- package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +17 -0
- package/assets/.appweaver/scripts/powershell/aw-parity.ps1 +13 -0
- package/assets/.appweaver/scripts/powershell/aw-rev-constitution.ps1 +13 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +2232 -0
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +15 -0
- package/assets/.appweaver/scripts/python/aw-component-catalog.py +121 -0
- package/assets/.appweaver/scripts/python/aw-inventory.py +1180 -0
- package/assets/.appweaver/scripts/python/aw-parity.py +380 -0
- package/assets/.appweaver/scripts/python/aw-rev-constitution.py +329 -0
- package/assets/.appweaver/scripts/python/aw-stack.py +1251 -0
- package/assets/.appweaver/scripts/python/aw-xref.py +1330 -0
- package/assets/.appweaver/scripts/python/validate_okf.py +352 -0
- package/assets/.appweaver/stack-profiles/angular.json +16 -0
- package/assets/.appweaver/stack-profiles/blazor-wasm.json +17 -0
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +15 -0
- package/assets/.appweaver/stack-profiles/go-chi.json +14 -0
- package/assets/.appweaver/stack-profiles/java-spring.json +16 -0
- package/assets/.appweaver/stack-profiles/nextjs.json +16 -0
- package/assets/.appweaver/stack-profiles/node-express.json +15 -0
- package/assets/.appweaver/stack-profiles/python-fastapi.json +16 -0
- package/assets/.appweaver/stack-profiles/react-vite.json +17 -0
- package/assets/.appweaver/stack-profiles/sveltekit.json +16 -0
- package/assets/.appweaver/stack-profiles/vue-vite.json +17 -0
- package/assets/.appweaver/stacks.json +3 -0
- package/assets/.appweaver/templates/constitution-domain-template.md +116 -0
- package/assets/.appweaver/templates/constitution-technical-template.md +108 -0
- package/assets/.appweaver/templates/constitution-ui-template.md +116 -0
- package/assets/.appweaver/templates/delta-design-template.md +69 -0
- package/assets/.appweaver/templates/delta-spec-template.md +63 -0
- package/assets/.appweaver/templates/design-template.md +383 -0
- package/assets/.appweaver/templates/okf-concept-template.md +114 -0
- package/assets/.appweaver/templates/parity-manifest-template.json +49 -0
- package/assets/.appweaver/templates/source-map-template.md +85 -0
- package/assets/.appweaver/templates/spec-template.md +309 -0
- package/assets/.appweaver/templates/ui-design-template.md +273 -0
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.Reference.pack.txt +25248 -0
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +5726 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-foundation.ps1 +145 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-foundation.sh +196 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-reference-foundation.ps1 +157 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-reference-foundation.sh +209 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-foundation.ps1 +126 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-foundation.sh +217 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-reference-foundation.ps1 +129 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-reference-foundation.sh +218 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.Reference.pack.txt +19318 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +2833 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-foundation.ps1 +145 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-foundation.sh +196 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-reference-foundation.ps1 +157 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-reference-foundation.sh +209 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-foundation.ps1 +126 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-foundation.sh +217 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-reference-foundation.ps1 +129 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-reference-foundation.sh +218 -0
- package/assets/foundations/dotnet/components/Email/README.md +119 -0
- package/assets/foundations/dotnet/components/Email/ReferenceEndpoints/SendEmailEndpoint.cs +84 -0
- package/assets/foundations/dotnet/components/Email/integration.md +300 -0
- package/assets/foundations/dotnet/components/Jobs/README.md +113 -0
- package/assets/foundations/dotnet/components/Jobs/integration.md +259 -0
- package/assets/foundations/dotnet/components/QrGenerator/README.md +85 -0
- package/assets/foundations/dotnet/components/QrGenerator/ReferenceEndpoints/GenerateQrEndpoint.cs +57 -0
- package/assets/foundations/dotnet/components/QrGenerator/integration.md +230 -0
- package/assets/foundations/dotnet/components/README.md +74 -0
- package/assets/guardrails/README.md +222 -0
- package/assets/guardrails/domain/healthcare/ai-rules.domain.md +34 -0
- package/assets/guardrails/domain/healthcare/api-versioning.domain.md +17 -0
- package/assets/guardrails/domain/healthcare/auditing.domain.md +97 -0
- package/assets/guardrails/domain/healthcare/authorization.domain.md +26 -0
- package/assets/guardrails/domain/healthcare/clinical-terms.md +20 -0
- package/assets/guardrails/domain/healthcare/coding-standards.domain.md +26 -0
- package/assets/guardrails/domain/healthcare/consent.md +19 -0
- package/assets/guardrails/domain/healthcare/cqrs.domain.md +12 -0
- package/assets/guardrails/domain/healthcare/data-classification.domain.md +28 -0
- package/assets/guardrails/domain/healthcare/dependency-policy.domain.md +11 -0
- package/assets/guardrails/domain/healthcare/dicom.md +15 -0
- package/assets/guardrails/domain/healthcare/domain-constitution.md +119 -0
- package/assets/guardrails/domain/healthcare/domain-driven-design.domain.md +28 -0
- package/assets/guardrails/domain/healthcare/eventing.domain.md +18 -0
- package/assets/guardrails/domain/healthcare/fhir.md +36 -0
- package/assets/guardrails/domain/healthcare/folder-structure.domain.md +25 -0
- package/assets/guardrails/domain/healthcare/hipaa.md +34 -0
- package/assets/guardrails/domain/healthcare/hl7.md +15 -0
- package/assets/guardrails/domain/healthcare/ihe-conformance.md +31 -0
- package/assets/guardrails/domain/healthcare/integration-terms.md +21 -0
- package/assets/guardrails/domain/healthcare/persistence.domain.md +20 -0
- package/assets/guardrails/domain/healthcare/terminology.md +30 -0
- package/assets/guardrails/domain/healthcare/testing.domain.md +15 -0
- package/assets/guardrails/domain/healthcare/ux/accessibility-standards.md +36 -0
- package/assets/guardrails/domain/healthcare/ux/content-design.md +32 -0
- package/assets/guardrails/domain/healthcare/ux/interaction-states.md +44 -0
- package/assets/guardrails/domain/healthcare/ux/journey-mapping.md +40 -0
- package/assets/guardrails/domain/healthcare/vertical-slices.domain.md +18 -0
- package/assets/guardrails/domain/healthcare/zero-trust.domain.md +12 -0
- package/assets/guardrails/domain/pharma/ai-rules.domain.md +32 -0
- package/assets/guardrails/domain/pharma/api-versioning.domain.md +20 -0
- package/assets/guardrails/domain/pharma/atc.md +22 -0
- package/assets/guardrails/domain/pharma/auditing.domain.md +90 -0
- package/assets/guardrails/domain/pharma/authorization.domain.md +25 -0
- package/assets/guardrails/domain/pharma/coding-standards.domain.md +28 -0
- package/assets/guardrails/domain/pharma/cqrs.domain.md +16 -0
- package/assets/guardrails/domain/pharma/data-classification.domain.md +24 -0
- package/assets/guardrails/domain/pharma/data-provenance.md +21 -0
- package/assets/guardrails/domain/pharma/dependency-policy.domain.md +11 -0
- package/assets/guardrails/domain/pharma/domain-constitution.md +113 -0
- package/assets/guardrails/domain/pharma/domain-driven-design.domain.md +36 -0
- package/assets/guardrails/domain/pharma/domain-requirements.sample.md +103 -0
- package/assets/guardrails/domain/pharma/eventing.domain.md +18 -0
- package/assets/guardrails/domain/pharma/fhir.md +31 -0
- package/assets/guardrails/domain/pharma/folder-structure.domain.md +26 -0
- package/assets/guardrails/domain/pharma/hl7.md +17 -0
- package/assets/guardrails/domain/pharma/idmp.md +26 -0
- package/assets/guardrails/domain/pharma/integration-terms.md +21 -0
- package/assets/guardrails/domain/pharma/ndc.md +28 -0
- package/assets/guardrails/domain/pharma/persistence.domain.md +23 -0
- package/assets/guardrails/domain/pharma/pharma-terms.md +27 -0
- package/assets/guardrails/domain/pharma/product-lifecycle.domain.md +30 -0
- package/assets/guardrails/domain/pharma/regulatory-compliance.md +19 -0
- package/assets/guardrails/domain/pharma/terminology.md +32 -0
- package/assets/guardrails/domain/pharma/testing.domain.md +19 -0
- package/assets/guardrails/domain/pharma/vertical-slices.domain.md +17 -0
- package/assets/guardrails/domain/pharma/zero-trust.domain.md +12 -0
- package/assets/guardrails/projects/dotnet-default-project/domain/domain-requirements.md +58 -0
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +113 -0
- package/assets/guardrails/projects/dotnet-default-project/frontend/ui-requirements.md +43 -0
- package/assets/guardrails/projects/dotnet-default-project/service/technical-requirements.md +40 -0
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +263 -0
- package/assets/guardrails/requirement-spec.txt +27 -0
- package/assets/guardrails/technology/dotnet-webapi/PACKAGING.md +184 -0
- package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +44 -0
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +96 -0
- package/assets/guardrails/technology/dotnet-webapi/authorization-matrix.md +14 -0
- package/assets/guardrails/technology/dotnet-webapi/authorization.md +39 -0
- package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +54 -0
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +87 -0
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +63 -0
- package/assets/guardrails/technology/dotnet-webapi/data-classification.md +38 -0
- package/assets/guardrails/technology/dotnet-webapi/dependency-policy.md +40 -0
- package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +91 -0
- package/assets/guardrails/technology/dotnet-webapi/eventing.md +57 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/api-versioning.md +75 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/auditing.md +84 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/authorization.md +68 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/clean-architecture.md +37 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +97 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +141 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/eventing.md +46 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/persistence.md +207 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/vertical-slices.md +151 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/zero-trust.md +48 -0
- package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +61 -0
- package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +47 -0
- package/assets/guardrails/technology/dotnet-webapi/persistence.md +82 -0
- package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +162 -0
- package/assets/guardrails/technology/dotnet-webapi/testing.md +55 -0
- package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +70 -0
- package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +48 -0
- package/assets/guardrails/technology/java-spring/README.md +62 -0
- package/assets/guardrails/technology/nextjs/accessibility-implementation.md +56 -0
- package/assets/guardrails/technology/nextjs/api-client.md +56 -0
- package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +174 -0
- package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +203 -0
- package/assets/guardrails/technology/nextjs/auth-session.md +68 -0
- package/assets/guardrails/technology/nextjs/client-security.md +69 -0
- package/assets/guardrails/technology/nextjs/component-architecture.md +60 -0
- package/assets/guardrails/technology/nextjs/component-conventions.md +58 -0
- package/assets/guardrails/technology/nextjs/design-system.md +60 -0
- package/assets/guardrails/technology/nextjs/error-handling.md +72 -0
- package/assets/guardrails/technology/nextjs/forms-and-validation.md +75 -0
- package/assets/guardrails/technology/nextjs/frontend-testing.md +94 -0
- package/assets/guardrails/technology/nextjs/i18n.md +57 -0
- package/assets/guardrails/technology/nextjs/observability.md +67 -0
- package/assets/guardrails/technology/nextjs/performance-budgets.md +66 -0
- package/assets/guardrails/technology/nextjs/routing-navigation.md +52 -0
- package/assets/guardrails/technology/nextjs/state-management.md +70 -0
- package/assets/guardrails/technology/nextjs/ui-constitution.md +166 -0
- package/package.json +1 -1
- package/assets/docs/pharma-constitution.my-ref.md +0 -86
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Vertical Slice Standards — canonical example
|
|
2
|
+
|
|
3
|
+
> The worked `<SampleProjectName>` example for
|
|
4
|
+
> `guardrails/technology/dotnet-webapi/vertical-slices.md`. It is reference material for the
|
|
5
|
+
> phases that generate code — mapped for `design` — and carries no rule of its own: where this
|
|
6
|
+
> example and the rule file disagree, the rule file wins.
|
|
7
|
+
>
|
|
8
|
+
> It is deliberately absent from the `## /aw-spec-constitution` set. The constitution is a
|
|
9
|
+
> thin authority document — one declarative line and a `→` pointer per principle — and has no
|
|
10
|
+
> use for sample code.
|
|
11
|
+
|
|
12
|
+
## Canonical example (agent reference — `<SampleProjectName>` sample)
|
|
13
|
+
|
|
14
|
+
A complete slice: Endpoint (+ request class in the same file), Validator, DTO, and mapping.
|
|
15
|
+
|
|
16
|
+
```csharp
|
|
17
|
+
// <SampleProjectName>.Service/Projects/Features/CreateProject/CreateProjectEndpoint.cs
|
|
18
|
+
public class CreateProjectEndpoint(IMediator _mediator)
|
|
19
|
+
: SecureFastEndpoint<CreateProjectRequest, ProjectDto>
|
|
20
|
+
{
|
|
21
|
+
public override void Configure()
|
|
22
|
+
{
|
|
23
|
+
Post("/projects"); // kebab-case, plural noun
|
|
24
|
+
Summary(s => s.Summary = "Create a new project");
|
|
25
|
+
Options(o => o.WithTags("02 - Project - (FastEndpoints)"));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected override SecurityCachePolicy GetSecurityCachePolicy() => new()
|
|
29
|
+
{
|
|
30
|
+
SecurityLevel = SecurityLevel.Internal,
|
|
31
|
+
CachePolicy = CachePolicy.NoStore // mutating endpoint
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
protected override async Task<Result<ProjectDto>> ExecuteAsync(
|
|
35
|
+
CreateProjectRequest req, CancellationToken ct)
|
|
36
|
+
{
|
|
37
|
+
var result = await _mediator.Send(new CreateProjectCommand( // IMediator only
|
|
38
|
+
req.Code, req.Name, req.Description, req.StartDate, req.EndDate, req.OwnerId), ct);
|
|
39
|
+
|
|
40
|
+
if (result.IsSuccess && !string.IsNullOrWhiteSpace(result.Value?.RowVersionBase64))
|
|
41
|
+
HttpContext.Response.SetETagFromBase64(result.Value!.RowVersionBase64);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Request lives in the SAME file — primitive types only
|
|
47
|
+
public class CreateProjectRequest
|
|
48
|
+
{
|
|
49
|
+
public string Code { get; set; } = string.Empty;
|
|
50
|
+
public string Name { get; set; } = string.Empty;
|
|
51
|
+
public string? Description { get; set; }
|
|
52
|
+
public DateOnly StartDate { get; set; }
|
|
53
|
+
public DateOnly? EndDate { get; set; }
|
|
54
|
+
public Guid? OwnerId { get; set; }
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```csharp
|
|
59
|
+
// .../CreateProject/CreateProjectValidator.cs — request shape only
|
|
60
|
+
public class CreateProjectValidator : AbstractValidator<CreateProjectCommand>
|
|
61
|
+
{
|
|
62
|
+
public CreateProjectValidator()
|
|
63
|
+
{
|
|
64
|
+
RuleFor(x => x.Code).NotEmpty().MaximumLength(20).Matches(@"^[A-Z0-9\-]+$");
|
|
65
|
+
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
|
|
66
|
+
RuleFor(x => x.EndDate)
|
|
67
|
+
.GreaterThanOrEqualTo(x => x.StartDate).When(x => x.EndDate.HasValue);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```csharp
|
|
73
|
+
// {Module}/Shared/ProjectDto.cs — includes RowVersionBase64 (IConcurrencyTracked)
|
|
74
|
+
public record ProjectDto
|
|
75
|
+
{
|
|
76
|
+
public Guid Id { get; set; }
|
|
77
|
+
public string Code { get; set; } = string.Empty;
|
|
78
|
+
public string Name { get; set; } = string.Empty;
|
|
79
|
+
public string Status { get; set; } = string.Empty;
|
|
80
|
+
public Guid? OwnerId { get; set; }
|
|
81
|
+
public List<ProjectTaskDto> Tasks { get; set; } = new();
|
|
82
|
+
public string RowVersionBase64 { get; set; } = string.Empty;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// {Module}/Shared/ProjectMappingExtensions.cs — hand-written, zero reflection
|
|
86
|
+
public static class ProjectMappingExtensions
|
|
87
|
+
{
|
|
88
|
+
public static ProjectDto ToDto(this Project e) => new()
|
|
89
|
+
{
|
|
90
|
+
Id = e.Id.Value, Code = e.Code.Value, Name = e.Name,
|
|
91
|
+
Status = e.Status.ToString(), OwnerId = e.OwnerId?.Value,
|
|
92
|
+
Tasks = e.Tasks.Select(t => t.ToDto()).ToList(),
|
|
93
|
+
RowVersionBase64 = e.RowVersion is { Length: > 0 } rv
|
|
94
|
+
? Convert.ToBase64String(rv) : string.Empty
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
public static List<ProjectDto> ToDtoList(this IEnumerable<Project> es)
|
|
98
|
+
=> es.Select(e => e.ToDto()).ToList();
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Sub-resource endpoint for a distinct operation on a child — nested route, multiple route
|
|
103
|
+
params, `EmptyResponse` when there is no body to return:
|
|
104
|
+
|
|
105
|
+
```csharp
|
|
106
|
+
// .../Features/AssignTask/AssignTaskEndpoint.cs
|
|
107
|
+
public class AssignTaskEndpoint(IMediator _mediator)
|
|
108
|
+
: SecureFastEndpoint<AssignTaskRequest, EmptyResponse>
|
|
109
|
+
{
|
|
110
|
+
public override void Configure()
|
|
111
|
+
{
|
|
112
|
+
Put("/projects/{projectId:guid}/tasks/{taskId:guid}/assignee"); // sub-resource
|
|
113
|
+
Summary(s => s.Summary = "Assign a task to a TaskUser");
|
|
114
|
+
Options(o => o.WithTags("02 - Project - (FastEndpoints)"));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected override SecurityCachePolicy GetSecurityCachePolicy() => new()
|
|
118
|
+
{ SecurityLevel = SecurityLevel.Internal, CachePolicy = CachePolicy.NoStore };
|
|
119
|
+
|
|
120
|
+
protected override async Task<Result<EmptyResponse>> ExecuteAsync(
|
|
121
|
+
AssignTaskRequest req, CancellationToken ct)
|
|
122
|
+
{
|
|
123
|
+
var projectId = Route<Guid>("projectId");
|
|
124
|
+
var taskId = Route<Guid>("taskId");
|
|
125
|
+
var result = await _mediator.Send(new AssignTaskCommand(projectId, taskId, req.AssigneeId), ct);
|
|
126
|
+
return result.IsSuccess
|
|
127
|
+
? Result<EmptyResponse>.Ok(new EmptyResponse())
|
|
128
|
+
: Result<EmptyResponse>.Failure([.. result.Errors]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public class AssignTaskRequest { public Guid AssigneeId { get; set; } }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Standard `PagedResult<T>` contract (required init props, computed `TotalPages`):
|
|
136
|
+
|
|
137
|
+
```csharp
|
|
138
|
+
// {Module}/Common/PagedResult.cs
|
|
139
|
+
public sealed class PagedResult<T>
|
|
140
|
+
{
|
|
141
|
+
public required IReadOnlyList<T> Items { get; init; }
|
|
142
|
+
public required int TotalCount { get; init; }
|
|
143
|
+
public required int PageNumber { get; init; }
|
|
144
|
+
public required int PageSize { get; init; }
|
|
145
|
+
public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Related
|
|
150
|
+
|
|
151
|
+
- Rule file: `guardrails/technology/dotnet-webapi/vertical-slices.md`
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Zero-Trust Standards — canonical example
|
|
2
|
+
|
|
3
|
+
> The worked `<SampleProjectName>` example for
|
|
4
|
+
> `guardrails/technology/dotnet-webapi/zero-trust.md`. It is reference material for the phases
|
|
5
|
+
> that generate code — mapped for `design` — and carries no rule of its own: where this
|
|
6
|
+
> example and the rule file disagree, the rule file wins.
|
|
7
|
+
>
|
|
8
|
+
> It is deliberately absent from the `## /aw-spec-constitution` set. The constitution is a
|
|
9
|
+
> thin authority document — one declarative line and a `→` pointer per principle — and has no
|
|
10
|
+
> use for sample code.
|
|
11
|
+
|
|
12
|
+
## Canonical example (agent reference — `<SampleProjectName>` sample)
|
|
13
|
+
|
|
14
|
+
Secrets/IdP config read from configuration (Aspire parameters / secrets manager) — never
|
|
15
|
+
hard-coded; explicit CORS allow-list; HTTPS enforced outside development:
|
|
16
|
+
|
|
17
|
+
```csharp
|
|
18
|
+
// <SampleProjectName>.Service/Program.cs
|
|
19
|
+
var cfg = builder.Configuration;
|
|
20
|
+
|
|
21
|
+
// IdP settings come from config — no client secrets or issuer URLs in source (see [[authorization]])
|
|
22
|
+
builder.Services.AddAppWeaverSecurity(security => security
|
|
23
|
+
.AddAuthentication(auth => auth.UseAzureAdB2C(o =>
|
|
24
|
+
{
|
|
25
|
+
o.Instance = cfg["AzureAdB2C:Instance"]!;
|
|
26
|
+
o.Domain = cfg["AzureAdB2C:Domain"]!;
|
|
27
|
+
o.ClientId = cfg["AzureAdB2C:ClientId"]!;
|
|
28
|
+
}, cfg))
|
|
29
|
+
.AddAuthorization(authz => authz.UseRoleBased(o => o.AdminRole = "Admin")),
|
|
30
|
+
options => { }, configuration: cfg);
|
|
31
|
+
|
|
32
|
+
// Explicit CORS allow-list — no wildcards
|
|
33
|
+
builder.Services.AddCors(o => o.AddPolicy("DevelopmentPolicy", p => p
|
|
34
|
+
.WithOrigins("http://localhost:7000", "http://localhost:3000")
|
|
35
|
+
.AllowAnyMethod().AllowAnyHeader().AllowCredentials()));
|
|
36
|
+
|
|
37
|
+
var app = builder.Build();
|
|
38
|
+
app.UseCors("DevelopmentPolicy");
|
|
39
|
+
app.UseAuthentication();
|
|
40
|
+
app.UseAuthorization();
|
|
41
|
+
|
|
42
|
+
if (!app.Environment.IsEnvironment("Development"))
|
|
43
|
+
app.UseHttpsRedirection(); // enforce HTTPS outside dev
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Related
|
|
47
|
+
|
|
48
|
+
- Rule file: `guardrails/technology/dotnet-webapi/zero-trust.md`
|
|
@@ -0,0 +1,61 @@
|
|
|
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-design`, `/aw-spec-implement`, `/aw-spec-implement-delta`.
|
|
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
|
+
guardrails/ 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]]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Modular Monolith Standards
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic). NON-NEGOTIABLE where marked. Commands: `/aw-spec-design`.
|
|
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
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Persistence Standards
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic) — domain additions in [[persistence.domain]]. NON-NEGOTIABLE where marked. Commands: `/aw-spec-design`, `/aw-spec-run`.
|
|
4
|
+
|
|
5
|
+
## EF Core conventions
|
|
6
|
+
|
|
7
|
+
- The `DbContext` is a `ContextAwareDbContext<TContext>` and is **provider-aware**.
|
|
8
|
+
- EF Core mapping lives **only** in `{Module}/Persistence/<Aggregate>Configuration.cs`,
|
|
9
|
+
applied via `ApplyConfigurationsFromAssembly`.
|
|
10
|
+
- `DateTimeOffset` columns **MUST** be re-mapped to `timestamptz` when the provider is Npgsql.
|
|
11
|
+
- Cross-cutting behaviors are applied via `modelBuilder.ApplyCrossCuttingBehaviors(this)`:
|
|
12
|
+
soft-delete global filters, tenant filters, base concurrency-token configuration.
|
|
13
|
+
|
|
14
|
+
## Repository rules
|
|
15
|
+
|
|
16
|
+
- **Read handlers** inject `IReadRepository<TAggregate>`; **write handlers** inject
|
|
17
|
+
`IRepository<TAggregate>` + `IUnitOfWork`.
|
|
18
|
+
- No "repository as data-access god object" — **no custom methods** beyond
|
|
19
|
+
`IRepository<T>` / `IReadRepository<T>` + `Specification<T>`.
|
|
20
|
+
- Direct `DbContext.Set<T>()` LINQ inside Application Features is FORBIDDEN — use
|
|
21
|
+
`IReadRepository<T>` + a `Specification<T>` only.
|
|
22
|
+
|
|
23
|
+
## Specification pattern
|
|
24
|
+
|
|
25
|
+
- Every non-trivial query is encapsulated in a `Specification<TAggregate>` under
|
|
26
|
+
`{Module}/Domain/Specifications/`.
|
|
27
|
+
- Specifications **MUST be composable**; paging via `WithPaging(pageNumber, pageSize)`.
|
|
28
|
+
- Ordering uses shared generic helpers (`OrderByIdAsc`, `OrderByIdDesc`, `OrderByNameAsc`);
|
|
29
|
+
ad-hoc `OrderBy` in handlers is forbidden when a typed helper exists.
|
|
30
|
+
- **Cross-aggregate joins via EF navigation properties are FORBIDDEN** — reference parent
|
|
31
|
+
aggregates by strongly-typed ID and project client-side.
|
|
32
|
+
|
|
33
|
+
## Concurrency handling
|
|
34
|
+
|
|
35
|
+
- Every mutable aggregate **MUST** implement `IConcurrencyTracked`.
|
|
36
|
+
- Concurrency tokens are **client-managed**: `byte[] RowVersion`,
|
|
37
|
+
`IsConcurrencyToken().ValueGeneratedNever()`, refreshed by a `SaveChanges` interceptor.
|
|
38
|
+
- SQL Server `IsRowVersion()` semantics **MUST NOT** leak into entity configurations.
|
|
39
|
+
- ETag / `If-Match` at the edge: see [[api-versioning]].
|
|
40
|
+
- Server sets the ETag header on every create/read/update via `SetETagFromBase64(...)`.
|
|
41
|
+
- Update/Delete endpoints extract `If-Match` via `GetIfMatchBase64Token()` and pass it
|
|
42
|
+
to the command; mismatch returns `409 concurrency.conflict`.
|
|
43
|
+
- `DbUpdateConcurrencyException` is translated to the same `409 concurrency.conflict`.
|
|
44
|
+
|
|
45
|
+
## Soft-delete rules
|
|
46
|
+
|
|
47
|
+
- Soft delete is enabled via `EnableSoftDelete()`.
|
|
48
|
+
- **Hard deletes are FORBIDDEN** for any aggregate flagged as retention-protected / regulated;
|
|
49
|
+
deletion is logical via `MarkAsDeleted(...)`. (See [[persistence.domain]] for which data
|
|
50
|
+
classes are retention-protected.)
|
|
51
|
+
- Append-only aggregates have no delete path at all.
|
|
52
|
+
|
|
53
|
+
## Tenancy patterns
|
|
54
|
+
|
|
55
|
+
- Tenancy is enabled via `EnableTenancy()` and `RegisterDefaultContexts(...)`.
|
|
56
|
+
- Tenant resolution **MUST be transparent** to handlers.
|
|
57
|
+
- Default model: **tenant-per-row** with tenant-aware global filters; deployments MAY opt
|
|
58
|
+
into tenant-per-database via configuration.
|
|
59
|
+
|
|
60
|
+
## Migrations policy
|
|
61
|
+
|
|
62
|
+
- Migrations are authored against **PostgreSQL** (production provider). Development MAY run
|
|
63
|
+
on SQLite (non-sensitive development data only).
|
|
64
|
+
- Multi-provider warnings (`PendingModelChangesWarning`) **MUST** be suppressed only at the
|
|
65
|
+
runtime registration site, **never** in migration code.
|
|
66
|
+
- Migrations **MUST** apply cleanly to a fresh PostgreSQL instance (CI gate).
|
|
67
|
+
|
|
68
|
+
## Provider profile
|
|
69
|
+
|
|
70
|
+
- Production: PostgreSQL 16+ (encryption-at-rest profile in [[persistence.domain]]);
|
|
71
|
+
per-module schemas where reasonable.
|
|
72
|
+
- Development: PostgreSQL via Aspire containers; SQLite only for in-memory unit-test fixtures.
|
|
73
|
+
|
|
74
|
+
## Canonical example
|
|
75
|
+
|
|
76
|
+
The worked `<SampleProjectName>` example for these rules lives in
|
|
77
|
+
`guardrails/technology/dotnet-webapi/examples/persistence.md`, mapped for `design` and `run`.
|
|
78
|
+
The constitution set maps the rules without it.
|
|
79
|
+
|
|
80
|
+
## Related
|
|
81
|
+
|
|
82
|
+
- [[clean-architecture]] · [[cqrs]] · [[domain-driven-design]] · [[api-versioning]] · [[persistence.domain]]
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Technology Constitution — dotnet-webapi (domain-agnostic base)
|
|
2
|
+
|
|
3
|
+
> The technology-only governance base for this stack corpus. It heads the set mapped under
|
|
4
|
+
> `## /aw-spec-constitution` in a project's `tech-rule-bindings.md` — this file, then the
|
|
5
|
+
> standard technical rules beside it, then the project team's `technical-requirements.md` —
|
|
6
|
+
> which `/appweaver constitution` ratifies into `.appweaver/memory/constitution.technical.md`.
|
|
7
|
+
|
|
8
|
+
This file is domain-agnostic on purpose: the constitution command loads it *before* the domain
|
|
9
|
+
is selected, and that ordering is what keeps the technical half free of domain material. The
|
|
10
|
+
project's three constitutional SETS are siblings, never merged here — each is headed by
|
|
11
|
+
its own base file:
|
|
12
|
+
|
|
13
|
+
| Source | Ratified into | Principle series |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `technology-constitution.md` (this file) | `constitution.technical.md` | I–XV |
|
|
16
|
+
| `domain-constitution.md` | `constitution.domain.md` | XVI+ |
|
|
17
|
+
| the lane corpus's `ui-constitution.md` | `constitution.ui.md` | UX-* |
|
|
18
|
+
|
|
19
|
+
- Core Principles **I–XV** below are domain-agnostic and reused across every project that maps this corpus.
|
|
20
|
+
- **PROJECT IDENTITY**, **REGULATORY REGIME**, domain principles (**XVI+**), domain folder
|
|
21
|
+
additions and governance-override values are **not** filled here — they come from
|
|
22
|
+
`domain-constitution.md`.
|
|
23
|
+
- **Experience principles (UX-\*)** are not filled here — they come from the lane corpus's
|
|
24
|
+
`ui-constitution.md`.
|
|
25
|
+
|
|
26
|
+
This content is the supreme engineering authority for the service lane: where any other
|
|
27
|
+
engineering guidance in the repository disagrees, this document wins. It stays slim and
|
|
28
|
+
authoritative — the detailed rules live in the technical rule files each principle cites.
|
|
29
|
+
|
|
30
|
+
## Runtime Style
|
|
31
|
+
|
|
32
|
+
A Modular Monolith built to evolve into independent services. It combines Clean Architecture,
|
|
33
|
+
Domain-Driven Design, CQRS + Mediator, Vertical Slice feature organization, and
|
|
34
|
+
Specification-Driven Development. Cross-cutting concerns are delegated to the AppWeaver
|
|
35
|
+
framework family; business logic lives inside aggregates inside their owning module.
|
|
36
|
+
|
|
37
|
+
## Core Principles (domain-agnostic)
|
|
38
|
+
|
|
39
|
+
> Essence only — the full rules live in the cited rule file.
|
|
40
|
+
|
|
41
|
+
### I — Modular Monolith with Vertical Slices (NON-NEGOTIABLE)
|
|
42
|
+
Single deployable service partitioned into modules; each module = Domain/Features/Persistence/Shared; each feature is a self-contained vertical slice; no cross-module Domain coupling; no horizontal service layers.
|
|
43
|
+
→ `guardrails/technology/dotnet-webapi/modular-monolith.md`, `vertical-slices.md`
|
|
44
|
+
|
|
45
|
+
### II — Clean Architecture Dependency Rule (NON-NEGOTIABLE)
|
|
46
|
+
Dependencies flow inward: API → Application → Domain ← Infrastructure. Domain is framework-agnostic; endpoints call `IMediator`, never repositories/DbContext.
|
|
47
|
+
→ `guardrails/technology/dotnet-webapi/clean-architecture.md`
|
|
48
|
+
|
|
49
|
+
### III — Domain-Driven Design with Rich Aggregates (NON-NEGOTIABLE)
|
|
50
|
+
Access entities through aggregate roots; strongly-typed Vogen IDs; sealed validating value objects; immutable domain events; invariants enforced in the aggregate; transaction boundary = aggregate boundary.
|
|
51
|
+
→ `guardrails/technology/dotnet-webapi/domain-driven-design.md`
|
|
52
|
+
|
|
53
|
+
### IV — CQRS via Mediator with Result Pattern (NON-NEGOTIABLE)
|
|
54
|
+
All logic dispatched via AppWeaver.Mediator; commands/queries return `Result<T>`; errors are `Result.Failure` (not exceptions); writes run in a transaction and publish domain events.
|
|
55
|
+
→ `guardrails/technology/dotnet-webapi/cqrs.md`
|
|
56
|
+
|
|
57
|
+
### V — Specification-Driven Persistence (NON-NEGOTIABLE)
|
|
58
|
+
Query composition lives in Domain Specifications; no ad-hoc LINQ in handlers; no cross-aggregate EF navigation joins; reference parents by strongly-typed ID.
|
|
59
|
+
→ `guardrails/technology/dotnet-webapi/persistence.md`
|
|
60
|
+
|
|
61
|
+
### VI — Immutable Contracts at the Edge
|
|
62
|
+
Shared DTOs mapped with hand-written `ToDto()`; no reflection mappers; `RowVersionBase64` on concurrency-tracked DTOs; standard `PagedResult<T>`; external projections stay parallel.
|
|
63
|
+
→ `guardrails/technology/dotnet-webapi/vertical-slices.md`
|
|
64
|
+
|
|
65
|
+
### VII — Optimistic Concurrency via ETag / If-Match (NON-NEGOTIABLE)
|
|
66
|
+
Mutable aggregates implement `IConcurrencyTracked`; ETag set on every create/read/update; `If-Match` required on update/delete; mismatch → 409 `concurrency.conflict`.
|
|
67
|
+
→ `guardrails/technology/dotnet-webapi/persistence.md`, `guardrails/technology/dotnet-webapi/api-versioning.md`
|
|
68
|
+
|
|
69
|
+
### VIII — Multi-Provider, Multi-Tenant Persistence
|
|
70
|
+
Provider-aware `ContextAwareDbContext`; PostgreSQL production / SQLite dev (non-sensitive); client-managed concurrency tokens; cross-cutting soft-delete/tenant/audit behaviors; hard delete forbidden for retention-protected data.
|
|
71
|
+
→ `guardrails/technology/dotnet-webapi/persistence.md`
|
|
72
|
+
|
|
73
|
+
### IX — Security by Default (NON-NEGOTIABLE)
|
|
74
|
+
`SecureFastEndpoint` + declared cache policy; external IdP only (no local identity); role-based **and** scope-based auth; secrets from a manager not appsettings; TLS 1.2+; explicit CORS; sensitive data encrypted in transit and at rest (CMK).
|
|
75
|
+
→ `guardrails/technology/dotnet-webapi/zero-trust.md`, `authorization.md`, `data-classification.md`
|
|
76
|
+
|
|
77
|
+
### X — Observability is a First-Class Citizen (NON-NEGOTIABLE)
|
|
78
|
+
AppWeaver.ServiceDefaults (OTel + Serilog + health + resilience + discovery); `ILogger` message templates only; never log secrets/regulated data; every sensitive read/mutation emits an audit record; trace context propagates everywhere.
|
|
79
|
+
→ `guardrails/technology/dotnet-webapi/auditing.md`, `data-classification.md`
|
|
80
|
+
|
|
81
|
+
### XI — Cloud-Native Orchestration via .NET Aspire
|
|
82
|
+
AppHost is the composition root; backing services are Aspire resources referenced via `.WithReference(...)`; no hard-coded connection strings/ports; Helm stays in sync with the AppHost projection.
|
|
83
|
+
→ `guardrails/technology/dotnet-webapi/zero-trust.md`, `eventing.md`
|
|
84
|
+
|
|
85
|
+
### XII — REST API Standards
|
|
86
|
+
Internal REST (FastEndpoints, one endpoint/file, kebab routes); standard paging; versioned breaking changes; machine-readable contracts (OpenAPI). Domain interoperability surfaces (e.g. FHIR) are added by the domain half.
|
|
87
|
+
→ `guardrails/technology/dotnet-webapi/api-versioning.md`, `vertical-slices.md`
|
|
88
|
+
|
|
89
|
+
### XIII — Test-First Engineering
|
|
90
|
+
Tests for every aggregate behavior/value object/specification/handler/endpoint; deterministic (`IClock`/`IGuidGenerator`); contract changes ship with test updates in the same commit.
|
|
91
|
+
→ `guardrails/technology/dotnet-webapi/testing.md`
|
|
92
|
+
|
|
93
|
+
### XIV — Data Integration Layer / Single Source of Truth (NON-NEGOTIABLE)
|
|
94
|
+
Authoritative system of record; consumers use the REST API / published projections / CloudEvents only; no direct DB access; inbound feeds flow through Interop adapters; versioned machine-readable schemas.
|
|
95
|
+
→ `guardrails/technology/dotnet-webapi/eventing.md`, `guardrails/technology/dotnet-webapi/api-versioning.md`
|
|
96
|
+
|
|
97
|
+
### XV — Evolutionary Path to Microservices
|
|
98
|
+
Modules own their aggregates/persistence/DTOs/projectors/events; cross-module access via Shared DTOs or `IMediator`; per-module schemas recommended; extraction approved by amendment.
|
|
99
|
+
→ `guardrails/technology/dotnet-webapi/modular-monolith.md`
|
|
100
|
+
|
|
101
|
+
> **Domain principles (XVI and beyond) are supplied by `domain-constitution.md`.**
|
|
102
|
+
> **Experience principles (UX-\*) are supplied by the lane corpus's `ui-constitution.md`.**
|
|
103
|
+
|
|
104
|
+
## Mandatory Folder Structure
|
|
105
|
+
|
|
106
|
+
Governed by the technical rule files; this file does not duplicate the tree.
|
|
107
|
+
|
|
108
|
+
- Technology layout (src/tests/helm/docs, module = Domain/Features/Persistence/Shared):
|
|
109
|
+
`guardrails/technology/dotnet-webapi/folder-structure.md`
|
|
110
|
+
- Domain-specific additions (Interop adapters, domain test fixtures, regulatory controls):
|
|
111
|
+
supplied by `domain-constitution.md` + the active domain corpus's `folder-structure.domain.md`
|
|
112
|
+
- The frontend lane's layout lives entirely under its own root and is supplied by that lane's
|
|
113
|
+
own corpus, `guardrails/technology/nextjs/ui-constitution.md`. Nothing here applies to it.
|
|
114
|
+
|
|
115
|
+
Project names use the `<SampleProjectName>` placeholder. Cross-cutting files (`Program.cs`,
|
|
116
|
+
`GlobalUsings.cs`, `Directory.Build.props`, `Directory.Packages.props`, `global.json`) MUST stay
|
|
117
|
+
at the same level they occupy in the AppWeaver template.
|
|
118
|
+
|
|
119
|
+
## Conventions, Forbidden Patterns, AI Rules, Quality Gates
|
|
120
|
+
|
|
121
|
+
Governed in full by the technical rule files; this file does not duplicate them.
|
|
122
|
+
|
|
123
|
+
- Naming & coding conventions, the Forbidden Patterns, and the CI Quality Gates:
|
|
124
|
+
`guardrails/technology/dotnet-webapi/coding-standards.md`
|
|
125
|
+
- Package governance (approved/banned packages, supply-chain, licensing):
|
|
126
|
+
`guardrails/technology/dotnet-webapi/dependency-policy.md`
|
|
127
|
+
- AI operational rules (generation, context loading, refusal conditions):
|
|
128
|
+
`.appweaver/ai-rules.md` — the domain half extends this with domain-specific masking,
|
|
129
|
+
code-set and refusal rules
|
|
130
|
+
|
|
131
|
+
## Governance
|
|
132
|
+
|
|
133
|
+
- This constitution supersedes all other engineering documentation in the repository. Where they
|
|
134
|
+
disagree, this document wins; a rule file that contradicts a principle here is the file that
|
|
135
|
+
gets corrected.
|
|
136
|
+
- Amendments are proposed via pull request that modifies this file (and the affected rule
|
|
137
|
+
files), includes a migration plan for non-compliant code, bumps the version per the policy
|
|
138
|
+
below, and is approved per the roles named in `domain-constitution.md`'s Governance Overrides
|
|
139
|
+
(at minimum one Principal Engineer + the domain's mandatory approver).
|
|
140
|
+
- Versioning follows SemVer applied to governance:
|
|
141
|
+
- **MAJOR** — removal or weakening of a NON-NEGOTIABLE principle, or a backward-incompatible
|
|
142
|
+
change to the Mandatory Folder Structure.
|
|
143
|
+
- **MINOR** — addition of a new principle, new mandatory section, or new bounded context.
|
|
144
|
+
- **PATCH** — clarifications, wording fixes, non-semantic edits.
|
|
145
|
+
- This file versions independently of `domain-constitution.md` and `ui-constitution.md`.
|
|
146
|
+
- Compliance review happens on every PR. Reviewers MUST cite the principle number when
|
|
147
|
+
requesting changes.
|
|
148
|
+
- A quarterly architecture and security review MUST audit a random sample of modules against
|
|
149
|
+
this constitution and file remediation tickets for any deviation.
|
|
150
|
+
- Runtime guidance files MUST carry a single pointer to the ratified constitution and MUST NOT
|
|
151
|
+
duplicate or paraphrase its rules.
|
|
152
|
+
- Domain-specific governance overrides (approver roles, regulatory review triggers, control
|
|
153
|
+
mappings) are supplied by `domain-constitution.md`.
|
|
154
|
+
|
|
155
|
+
## Ratification Metadata
|
|
156
|
+
|
|
157
|
+
> Carried into `constitution.technical.md` at ratification; the constitution command owns the
|
|
158
|
+
> version bump from there.
|
|
159
|
+
|
|
160
|
+
- **Version:** 1.0.0
|
|
161
|
+
- **Ratified:** `<YYYY-MM-DD>`
|
|
162
|
+
- **Last Amended:** `<YYYY-MM-DD>`
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Testing Standards
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic) — domain additions in [[testing.domain]]. Commands: `/aw-spec-design`, `/aw-spec-implement`, `/aw-spec-implement-delta`, `/aw-spec-analyze-slice`.
|
|
4
|
+
|
|
5
|
+
## Test-first engineering
|
|
6
|
+
|
|
7
|
+
- Generated tests follow Test-First Engineering and **MUST fail before** the production
|
|
8
|
+
change is in place.
|
|
9
|
+
- Any change to an aggregate invariant, endpoint contract, or error code **MUST** include
|
|
10
|
+
the corresponding test update **in the same commit**.
|
|
11
|
+
|
|
12
|
+
## Unit test conventions
|
|
13
|
+
|
|
14
|
+
- Unit tests are required for **every** aggregate behavior, value object, and specification.
|
|
15
|
+
- Tests are **deterministic** — time, randomness, and IDs are injected
|
|
16
|
+
(use the framework-provided `IClock` / `IGuidGenerator`). Never `DateTime.UtcNow` /
|
|
17
|
+
`Guid.NewGuid()` in the system under test.
|
|
18
|
+
- Tests **MUST NOT** reach into module internals; they exercise the public endpoint or
|
|
19
|
+
`IMediator` surface.
|
|
20
|
+
|
|
21
|
+
## Handler tests
|
|
22
|
+
|
|
23
|
+
Required for **every** Command/Query handler and MUST cover:
|
|
24
|
+
|
|
25
|
+
- happy path
|
|
26
|
+
- validation failure
|
|
27
|
+
- not-found
|
|
28
|
+
- (for writes) concurrency conflict
|
|
29
|
+
|
|
30
|
+
## Integration testing
|
|
31
|
+
|
|
32
|
+
- Required for **every** endpoint; run against **PostgreSQL via Testcontainers** or Aspire.
|
|
33
|
+
- Test database engine: Testcontainers PostgreSQL for integration; in-memory fixtures for
|
|
34
|
+
unit tests.
|
|
35
|
+
|
|
36
|
+
## Testcontainers usage
|
|
37
|
+
|
|
38
|
+
- Integration and contract tests spin up real PostgreSQL (and other backing services)
|
|
39
|
+
via Testcontainers / Aspire — no faked persistence in integration tests.
|
|
40
|
+
|
|
41
|
+
## Test data hygiene
|
|
42
|
+
|
|
43
|
+
- Tests use a **shared fixture library** rather than inlining fabricated data.
|
|
44
|
+
- Production data MUST NOT be used in development, test, demo, or shared CI.
|
|
45
|
+
(Domain specifics — synthetic fixtures + terminology sentinels — in [[testing.domain]].)
|
|
46
|
+
|
|
47
|
+
## Coverage rules
|
|
48
|
+
|
|
49
|
+
- Minimum **85% line coverage** on Application + Domain.
|
|
50
|
+
- **100%** on aggregate behaviors and value objects.
|
|
51
|
+
- Code coverage on changed files must meet the threshold (CI gate).
|
|
52
|
+
|
|
53
|
+
## Related
|
|
54
|
+
|
|
55
|
+
- [[domain-driven-design]] · [[persistence]] · [[testing.domain]]
|