@polymorphism-tech/morph-spec 4.7.0 → 4.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.morph/.morphversion +5 -0
- package/.morph/analytics/threads-log.jsonl +5 -0
- package/.morph/config/config.json +8 -0
- package/.morph/framework/agents.json +1815 -0
- package/.morph/framework/hooks/README.md +205 -0
- package/.morph/framework/hooks/claude-code/notification/approval-reminder.js +54 -0
- package/.morph/framework/hooks/claude-code/post-tool-use/dispatch.js +83 -0
- package/.morph/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +42 -0
- package/.morph/framework/hooks/claude-code/pre-compact/save-morph-context.js +61 -0
- package/.morph/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +71 -0
- package/.morph/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +58 -0
- package/.morph/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +64 -0
- package/.morph/framework/hooks/claude-code/session-start/inject-morph-context.js +94 -0
- package/.morph/framework/hooks/claude-code/statusline.py +538 -0
- package/.morph/framework/hooks/claude-code/statusline.sh +7 -0
- package/.morph/framework/hooks/claude-code/stop/validate-completion.js +88 -0
- package/.morph/framework/hooks/claude-code/user-prompt/enrich-prompt.js +91 -0
- package/.morph/framework/hooks/git/commit-msg/conventional-commits.sh +33 -0
- package/.morph/framework/hooks/git/pre-commit/agents.sh +25 -0
- package/.morph/framework/hooks/git/pre-commit/orchestrator.sh +64 -0
- package/.morph/framework/hooks/git/pre-commit/specs.sh +50 -0
- package/.morph/framework/hooks/git/pre-push/run-tests.sh +44 -0
- package/.morph/framework/hooks/shared/hook-response.js +45 -0
- package/.morph/framework/hooks/shared/phase-utils.js +129 -0
- package/.morph/framework/hooks/shared/state-reader.js +138 -0
- package/.morph/framework/hooks/shared/stdin-reader.js +26 -0
- package/.morph/framework/standards/STANDARDS.json +933 -0
- package/.morph/framework/standards/ai-agents/blazor-ui.md +364 -0
- package/.morph/framework/standards/ai-agents/production.md +415 -0
- package/.morph/framework/standards/ai-agents/setup.md +418 -0
- package/.morph/framework/standards/ai-agents/team-orchestration.md +479 -0
- package/.morph/framework/standards/ai-agents/workflows.md +354 -0
- package/.morph/framework/standards/architecture/ddd/aggregates.md +120 -0
- package/.morph/framework/standards/architecture/ddd/bounded-contexts.md +105 -0
- package/.morph/framework/standards/architecture/ddd/complexity-levels.md +108 -0
- package/.morph/framework/standards/architecture/ddd/entities.md +99 -0
- package/.morph/framework/standards/architecture/ddd/ubiquitous-language.md +58 -0
- package/.morph/framework/standards/architecture/ddd/value-objects.md +124 -0
- package/.morph/framework/standards/backend/api/minimal-api.md +494 -0
- package/.morph/framework/standards/backend/api/rest.md +492 -0
- package/.morph/framework/standards/backend/api/validation.md +88 -0
- package/.morph/framework/standards/backend/authentication/passkeys.md +428 -0
- package/.morph/framework/standards/backend/database/ef-core.md +199 -0
- package/.morph/framework/standards/backend/database/migrations.md +393 -0
- package/.morph/framework/standards/backend/database/postgresql/database.md +352 -0
- package/.morph/framework/standards/backend/database/repository-patterns.md +528 -0
- package/.morph/framework/standards/backend/database/vector-search-rag.md +541 -0
- package/.morph/framework/standards/backend/dotnet/async.md +366 -0
- package/.morph/framework/standards/backend/dotnet/core.md +117 -0
- package/.morph/framework/standards/backend/dotnet/di.md +439 -0
- package/.morph/framework/standards/backend/dotnet/program-cs-checklist.md +92 -0
- package/.morph/framework/standards/backend/integrations/asaas/asaas-api.md +216 -0
- package/.morph/framework/standards/backend/integrations/clerk/clerk-auth.md +290 -0
- package/.morph/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +350 -0
- package/.morph/framework/standards/backend/integrations/resend/resend-email.md +385 -0
- package/.morph/framework/standards/context/analytics.md +96 -0
- package/.morph/framework/standards/context/bundles.md +110 -0
- package/.morph/framework/standards/context/priming.md +78 -0
- package/.morph/framework/standards/core/architecture.md +185 -0
- package/.morph/framework/standards/core/coding.md +214 -0
- package/.morph/framework/standards/core/git-branching-strategy.md +403 -0
- package/.morph/framework/standards/core/git.md +185 -0
- package/.morph/framework/standards/core/testing.md +295 -0
- package/.morph/framework/standards/data/nosql/blob-storage.md +102 -0
- package/.morph/framework/standards/data/nosql/cache/redis.md +97 -0
- package/.morph/framework/standards/data/nosql/cosmos-db.md +118 -0
- package/.morph/framework/standards/data/vector-search/azure-ai-search.md +121 -0
- package/.morph/framework/standards/data/vector-search/rag-chunking.md +104 -0
- package/.morph/framework/standards/frontend/blazor/design-checklist.md +222 -0
- package/.morph/framework/standards/frontend/blazor/fluent-ui-setup.md +595 -0
- package/.morph/framework/standards/frontend/blazor/fluent-ui.md +137 -0
- package/.morph/framework/standards/frontend/blazor/html-conversion.md +184 -0
- package/.morph/framework/standards/frontend/blazor/lifecycle.md +195 -0
- package/.morph/framework/standards/frontend/blazor/pitfalls.md +198 -0
- package/.morph/framework/standards/frontend/blazor/state.md +191 -0
- package/.morph/framework/standards/frontend/design-system/animations.md +151 -0
- package/.morph/framework/standards/frontend/design-system/naming.md +64 -0
- package/.morph/framework/standards/frontend/nextjs/app-router.md +123 -0
- package/.morph/framework/standards/frontend/nextjs/components.md +132 -0
- package/.morph/framework/standards/frontend/nextjs/data-fetching.md +126 -0
- package/.morph/framework/standards/frontend/nextjs/forms.md +128 -0
- package/.morph/framework/standards/frontend/nextjs/naming-conventions.md +67 -0
- package/.morph/framework/standards/frontend/nextjs/nextjs-patterns.md +215 -0
- package/.morph/framework/standards/frontend/nextjs/project-structure.md +102 -0
- package/.morph/framework/standards/frontend/nextjs/state-management.md +72 -0
- package/.morph/framework/standards/frontend/nextjs/testing.md +111 -0
- package/.morph/framework/standards/infrastructure/azure/azure.md +624 -0
- package/.morph/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +422 -0
- package/.morph/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +516 -0
- package/.morph/framework/standards/infrastructure/azure/devops/local-development.md +520 -0
- package/.morph/framework/standards/infrastructure/azure/services/functions.md +486 -0
- package/.morph/framework/standards/infrastructure/azure/services/service-bus.md +459 -0
- package/.morph/framework/standards/infrastructure/azure/services/storage.md +407 -0
- package/.morph/framework/standards/infrastructure/docker/easypanel-deploy.md +196 -0
- package/.morph/framework/standards/infrastructure/supabase/mcp-setup.md +252 -0
- package/.morph/framework/standards/infrastructure/supabase/supabase-auth.md +176 -0
- package/.morph/framework/standards/infrastructure/supabase/supabase-pgvector.md +169 -0
- package/.morph/framework/standards/infrastructure/supabase/supabase-rls.md +184 -0
- package/.morph/framework/standards/infrastructure/supabase/supabase-storage.md +153 -0
- package/.morph/framework/standards/integration/api/graphql.md +91 -0
- package/.morph/framework/standards/integration/api/grpc.md +114 -0
- package/.morph/framework/standards/integration/api/rest-design.md +95 -0
- package/.morph/framework/standards/integration/event-driven/cqrs.md +101 -0
- package/.morph/framework/standards/integration/event-driven/event-sourcing.md +124 -0
- package/.morph/framework/standards/integration/event-driven/service-bus.md +95 -0
- package/.morph/framework/standards/integration/mcp/mcp-tools.md +384 -0
- package/.morph/framework/standards/observability/logging.md +131 -0
- package/.morph/framework/standards/observability/metrics.md +121 -0
- package/.morph/framework/standards/observability/monitoring.md +114 -0
- package/.morph/framework/standards/observability/tracing.md +132 -0
- package/.morph/framework/standards/workflows/parallel-execution.md +112 -0
- package/.morph/framework/standards/workflows/thread-management.md +113 -0
- package/.morph/framework/templates/.idea/morph-templates.xml +92 -0
- package/.morph/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/.morph/framework/templates/IDE-SNIPPETS.md +266 -0
- package/.morph/framework/templates/README.md +814 -0
- package/.morph/framework/templates/REGISTRY.json +1888 -0
- package/.morph/framework/templates/code/dotnet/backend/repository.cs +141 -0
- package/.morph/framework/templates/code/dotnet/backend/service.cs +139 -0
- package/.morph/framework/templates/code/dotnet/contracts/Commands.cs +74 -0
- package/.morph/framework/templates/code/dotnet/contracts/Entities.cs +25 -0
- package/.morph/framework/templates/code/dotnet/contracts/Queries.cs +74 -0
- package/.morph/framework/templates/code/dotnet/contracts/README.md +74 -0
- package/.morph/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -0
- package/.morph/framework/templates/code/dotnet/contracts/contracts-level1.cs +69 -0
- package/.morph/framework/templates/code/dotnet/contracts/contracts-level2.cs +86 -0
- package/.morph/framework/templates/code/dotnet/contracts/contracts-level3.cs +41 -0
- package/.morph/framework/templates/code/dotnet/database/migration.cs +83 -0
- package/.morph/framework/templates/code/dotnet/frontend/component.razor +239 -0
- package/.morph/framework/templates/code/dotnet/jobs/agent.cs +163 -0
- package/.morph/framework/templates/code/dotnet/jobs/job.cs +171 -0
- package/.morph/framework/templates/code/dotnet/test.cs +239 -0
- package/.morph/framework/templates/code/sql/rls-policy.sql +57 -0
- package/.morph/framework/templates/code/sql/supabase-migration.sql +100 -0
- package/.morph/framework/templates/code/sql/supabase-migration.template.sql +113 -0
- package/.morph/framework/templates/code/typescript/contracts.ts +168 -0
- package/.morph/framework/templates/context/CONTEXT-FEATURE.md +276 -0
- package/.morph/framework/templates/context/CONTEXT.md +181 -0
- package/.morph/framework/templates/docs/clarifications.md +253 -0
- package/.morph/framework/templates/docs/onboarding.md +123 -0
- package/.morph/framework/templates/docs/proposal.md +182 -0
- package/.morph/framework/templates/docs/schema-analysis.md +119 -0
- package/.morph/framework/templates/docs/spec.md +198 -0
- package/.morph/framework/templates/docs/ui-components.md +124 -0
- package/.morph/framework/templates/docs/ui-design-system.md +76 -0
- package/.morph/framework/templates/docs/ui-flows.md +167 -0
- package/.morph/framework/templates/docs/ui-mockups.md +98 -0
- package/.morph/framework/templates/docs/user-stories.md +34 -0
- package/.morph/framework/templates/examples/design-system-examples.md +357 -0
- package/.morph/framework/templates/examples/spec-examples.md +90 -0
- package/.morph/framework/templates/feature/decisions.md +187 -0
- package/.morph/framework/templates/feature/recap.md +146 -0
- package/.morph/framework/templates/feature/tasks.md +199 -0
- package/.morph/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -0
- package/.morph/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -0
- package/.morph/framework/templates/frontend/nextjs/env.mjs.hbs +32 -0
- package/.morph/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -0
- package/.morph/framework/templates/frontend/nextjs/page.tsx.hbs +22 -0
- package/.morph/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -0
- package/.morph/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -0
- package/.morph/framework/templates/infrastructure/azure/Dockerfile.example +82 -0
- package/.morph/framework/templates/infrastructure/azure/README.md +286 -0
- package/.morph/framework/templates/infrastructure/azure/app-insights.bicep +63 -0
- package/.morph/framework/templates/infrastructure/azure/app-service.bicep +164 -0
- package/.morph/framework/templates/infrastructure/azure/container-app-env.bicep +49 -0
- package/.morph/framework/templates/infrastructure/azure/container-app.bicep +156 -0
- package/.morph/framework/templates/infrastructure/azure/deploy-checklist.md +426 -0
- package/.morph/framework/templates/infrastructure/azure/deploy.ps1 +229 -0
- package/.morph/framework/templates/infrastructure/azure/deploy.sh +208 -0
- package/.morph/framework/templates/infrastructure/azure/key-vault.bicep +91 -0
- package/.morph/framework/templates/infrastructure/azure/main.bicep +189 -0
- package/.morph/framework/templates/infrastructure/azure/parameters.dev.json +29 -0
- package/.morph/framework/templates/infrastructure/azure/parameters.prod.json +29 -0
- package/.morph/framework/templates/infrastructure/azure/parameters.staging.json +29 -0
- package/.morph/framework/templates/infrastructure/azure/sql-database.bicep +103 -0
- package/.morph/framework/templates/infrastructure/azure/storage.bicep +106 -0
- package/.morph/framework/templates/infrastructure/docker/Dockerfile.template +58 -0
- package/.morph/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -0
- package/.morph/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -0
- package/.morph/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -0
- package/.morph/framework/templates/infrastructure/docker/easypanel.template.json +54 -0
- package/.morph/framework/templates/infrastructure/github/README.md +593 -0
- package/.morph/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +22 -0
- package/.morph/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -0
- package/.morph/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -0
- package/.morph/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +61 -0
- package/.morph/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +31 -0
- package/.morph/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -0
- package/.morph/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -0
- package/.morph/framework/templates/integrations/asaas-client.cs +387 -0
- package/.morph/framework/templates/integrations/asaas-webhook.cs +351 -0
- package/.morph/framework/templates/integrations/azure-identity-config.cs +288 -0
- package/.morph/framework/templates/integrations/clerk-config.cs +258 -0
- package/.morph/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -0
- package/.morph/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -0
- package/.morph/framework/templates/meta-prompts/hops/hop-retry.md +78 -0
- package/.morph/framework/templates/meta-prompts/hops/hop-validation.md +97 -0
- package/.morph/framework/templates/meta-prompts/hops/hop-wrapper.md +36 -0
- package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +113 -0
- package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +80 -0
- package/.morph/framework/templates/meta-prompts/squad-leaders/backend-squad.md +90 -0
- package/.morph/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +126 -0
- package/.morph/framework/templates/meta-prompts/squad-leaders/squad-leader.md +43 -0
- package/.morph/framework/templates/meta-prompts/validators/checkpoint-validator.md +107 -0
- package/.morph/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -0
- package/.morph/framework/templates/project-structure/dotnet-ddd.md +70 -0
- package/.morph/framework/templates/saas/subscription.cs +347 -0
- package/.morph/framework/templates/saas/tenant.cs +338 -0
- package/.morph/framework/templates/state.template.json +17 -0
- package/.morph/framework/templates/ui/FluentDesignTheme.cs +149 -0
- package/.morph/framework/templates/ui/MudTheme.cs +281 -0
- package/.morph/framework/templates/ui/design-system.css +226 -0
- package/.morph/logs/tool-failures.log +17 -0
- package/.morph/memory/pre-compact-2026-02-24T17-43-30-049Z.json +16 -0
- package/.morph/plans/eager-watching-bunny.md +105 -0
- package/.morph/plans/temporal-seeking-nebula.md +45 -0
- package/.morph/state.json +48 -0
- package/CLAUDE.md +1 -1
- package/README.md +119 -99
- package/bin/morph-spec.js +0 -9
- package/framework/CLAUDE.md +1 -1
- package/framework/hooks/README.md +10 -6
- package/framework/hooks/claude-code/notification/approval-reminder.js +2 -0
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +1 -1
- package/framework/hooks/claude-code/stop/validate-completion.js +1 -1
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +1 -1
- package/package.json +1 -1
- package/src/commands/project/init.js +15 -42
- package/src/commands/project/update.js +22 -37
- package/src/lib/installers/mcp-installer.js +18 -3
- package/src/utils/hooks-installer.js +5 -15
- package/src/commands/project/detect.js +0 -114
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# Microsoft Agent Framework - Workflow Orchestration Patterns
|
|
2
|
+
|
|
3
|
+
> **Scope:** universal
|
|
4
|
+
> **Layer:** 0 (always load)
|
|
5
|
+
> **Keywords:** workflow, orchestration, sequential, parallel, conditional
|
|
6
|
+
> **Load When:** always
|
|
7
|
+
|
|
8
|
+
**Ref:** `setup.md` — Core setup guide (packages, ChatClientAgent, tools)
|
|
9
|
+
|
|
10
|
+
Workflows compose multiple agents and executors into coordinated pipelines using `AgentWorkflowBuilder`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Core Concepts
|
|
15
|
+
|
|
16
|
+
| Concept | Description |
|
|
17
|
+
|---------|-------------|
|
|
18
|
+
| **Executor** | Base processing unit. Implements `IMessageHandler<T>`. Agents are executors. |
|
|
19
|
+
| **Edge** | Typed connection between executors defining message flow |
|
|
20
|
+
| **Workflow** | Composition of executors + edges forming a processing graph |
|
|
21
|
+
| **AIAgent** | Chat-based executor (most common type) |
|
|
22
|
+
|
|
23
|
+
### Packages
|
|
24
|
+
|
|
25
|
+
```xml
|
|
26
|
+
<PackageReference Include="Microsoft.Agents.AI" Version="1.0.0-preview.*" />
|
|
27
|
+
<PackageReference Include="Microsoft.Agents.AI.Workflows" Version="1.0.0-preview.*" />
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 5 Orchestration Patterns
|
|
33
|
+
|
|
34
|
+
| Pattern | Topology | Use Case | Coordination |
|
|
35
|
+
|---------|----------|----------|--------------|
|
|
36
|
+
| **Sequential** | Linear chain | Pipelines, multi-stage processing | Ordered, dependent |
|
|
37
|
+
| **Concurrent** | Broadcast | Parallel analysis, ensemble | Independent, aggregated |
|
|
38
|
+
| **GroupChat** | Star (manager hub) | Iterative refinement, review | Manager-controlled |
|
|
39
|
+
| **Handoff** | Mesh | Dynamic routing, escalation | Agent-decided |
|
|
40
|
+
| **Magentic** | Star (planner) | Complex generalist collaboration | Planner-based |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 1. Sequential Workflow
|
|
45
|
+
|
|
46
|
+
Agents execute in order — each agent receives the output of the previous one.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
[Writer] → [Editor] → [Output]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```csharp
|
|
53
|
+
using Microsoft.Agents.AI;
|
|
54
|
+
using Microsoft.Agents.AI.Workflows;
|
|
55
|
+
|
|
56
|
+
AIAgent writer = new ChatClientAgent(chatClient, new ChatClientAgentOptions
|
|
57
|
+
{
|
|
58
|
+
Name = "Writer",
|
|
59
|
+
Instructions = "Write stories that are engaging and creative."
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
AIAgent editor = new ChatClientAgent(chatClient, new ChatClientAgentOptions
|
|
63
|
+
{
|
|
64
|
+
Name = "Editor",
|
|
65
|
+
Instructions = "Make the story more engaging, fix grammar, and enhance plot."
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
Workflow workflow = AgentWorkflowBuilder.BuildSequential(writer, editor);
|
|
69
|
+
|
|
70
|
+
AIAgent workflowAgent = await workflow.AsAgentAsync();
|
|
71
|
+
AgentRunResponse response = await workflowAgent.RunAsync(
|
|
72
|
+
"Write a short story about a haunted house.");
|
|
73
|
+
|
|
74
|
+
Console.WriteLine(response.Text);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 2. Concurrent Workflow (Fan-Out / Fan-In)
|
|
80
|
+
|
|
81
|
+
Same input sent to multiple agents in parallel, results aggregated.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
┌→ [Agent B1] →┐
|
|
85
|
+
[Input] → Fan-Out → [Agent B2] → Fan-In → [Aggregator]
|
|
86
|
+
└→ [Agent B3] →┘
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```csharp
|
|
90
|
+
// Create specialized analysis agents
|
|
91
|
+
AIAgent securityAnalyst = chatClient.AsAIAgent(
|
|
92
|
+
instructions: "Analyze code for security vulnerabilities.",
|
|
93
|
+
name: "SecurityAnalyst");
|
|
94
|
+
|
|
95
|
+
AIAgent performanceAnalyst = chatClient.AsAIAgent(
|
|
96
|
+
instructions: "Analyze code for performance issues.",
|
|
97
|
+
name: "PerformanceAnalyst");
|
|
98
|
+
|
|
99
|
+
AIAgent readabilityAnalyst = chatClient.AsAIAgent(
|
|
100
|
+
instructions: "Analyze code for readability and maintainability.",
|
|
101
|
+
name: "ReadabilityAnalyst");
|
|
102
|
+
|
|
103
|
+
// Fan-out to all analysts, fan-in results
|
|
104
|
+
// Use AddFanOutEdge / AddFanInEdge for custom executors
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Note:** All executors in the same superstep must complete before downstream executors begin.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 3. GroupChat Workflow
|
|
112
|
+
|
|
113
|
+
Manager-controlled round-robin conversation between agents.
|
|
114
|
+
|
|
115
|
+
```csharp
|
|
116
|
+
Workflow workflow = AgentWorkflowBuilder
|
|
117
|
+
.CreateGroupChatBuilderWith(agents =>
|
|
118
|
+
new AgentWorkflowBuilder.RoundRobinGroupChatManager(agents)
|
|
119
|
+
{
|
|
120
|
+
MaximumIterationCount = 2 // Max rounds
|
|
121
|
+
})
|
|
122
|
+
.AddParticipants(writer, editor)
|
|
123
|
+
.Build();
|
|
124
|
+
|
|
125
|
+
AIAgent workflowAgent = await workflow.AsAgentAsync();
|
|
126
|
+
AgentRunResponse response = await workflowAgent.RunAsync(prompt);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 4. Handoff Workflow
|
|
132
|
+
|
|
133
|
+
Agents dynamically pass control based on routing rules. Mesh topology — no central orchestrator.
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
[Triage] ──┬→ [Math Tutor] ──→ [Triage]
|
|
137
|
+
└→ [History Tutor] → [Triage]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```csharp
|
|
141
|
+
// Define specialized agents
|
|
142
|
+
ChatClientAgent historyTutor = new(chatClient,
|
|
143
|
+
"You provide help with historical queries. Only respond about history.",
|
|
144
|
+
"history_tutor",
|
|
145
|
+
"Specialist agent for historical questions");
|
|
146
|
+
|
|
147
|
+
ChatClientAgent mathTutor = new(chatClient,
|
|
148
|
+
"You provide help with math problems. Only respond about math.",
|
|
149
|
+
"math_tutor",
|
|
150
|
+
"Specialist agent for math questions");
|
|
151
|
+
|
|
152
|
+
ChatClientAgent triageAgent = new(chatClient,
|
|
153
|
+
"You determine which agent to use. ALWAYS handoff to another agent.",
|
|
154
|
+
"triage_agent",
|
|
155
|
+
"Routes messages to the appropriate specialist");
|
|
156
|
+
|
|
157
|
+
// Build handoff workflow
|
|
158
|
+
var workflow = AgentWorkflowBuilder.StartHandoffWith(triageAgent)
|
|
159
|
+
.WithHandoffs(triageAgent, [mathTutor, historyTutor])
|
|
160
|
+
.WithHandoff(mathTutor, triageAgent)
|
|
161
|
+
.WithHandoff(historyTutor, triageAgent)
|
|
162
|
+
.Build();
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Running Handoff with Streaming Events
|
|
166
|
+
|
|
167
|
+
```csharp
|
|
168
|
+
List<ChatMessage> messages = new();
|
|
169
|
+
messages.Add(new(ChatRole.User, "What is the derivative of x^2?"));
|
|
170
|
+
|
|
171
|
+
StreamingRun run = await InProcessExecution.StreamAsync(workflow, messages);
|
|
172
|
+
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
|
173
|
+
|
|
174
|
+
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
|
175
|
+
{
|
|
176
|
+
if (evt is AgentResponseUpdateEvent e)
|
|
177
|
+
Console.WriteLine($"{e.ExecutorId}: {e.Data}");
|
|
178
|
+
else if (evt is WorkflowOutputEvent outputEvt)
|
|
179
|
+
{
|
|
180
|
+
messages.AddRange(((List<ChatMessage>)outputEvt.Data!).Skip(messages.Count));
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Handoff characteristics:**
|
|
187
|
+
- Receiving agent takes full task ownership
|
|
188
|
+
- Full conversation history passed to receiving agent
|
|
189
|
+
- Only supports `ChatAgent` with local tool execution
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 5. Magentic Workflow
|
|
194
|
+
|
|
195
|
+
Planner-based variant of GroupChat. A manager agent dynamically selects which agent acts next based on planning.
|
|
196
|
+
|
|
197
|
+
| Aspect | GroupChat | Magentic |
|
|
198
|
+
|--------|-----------|----------|
|
|
199
|
+
| Turn order | Fixed (round-robin) | Dynamic (planner decides) |
|
|
200
|
+
| Manager | Simple rotation | LLM-based planner |
|
|
201
|
+
| Best for | Predictable workflows | Complex, adaptive tasks |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Workflow-as-Agent
|
|
206
|
+
|
|
207
|
+
Any workflow can be encapsulated as a single agent for composition:
|
|
208
|
+
|
|
209
|
+
```csharp
|
|
210
|
+
Workflow workflow = AgentWorkflowBuilder.BuildSequential(writer, editor);
|
|
211
|
+
AIAgent workflowAgent = await workflow.AsAgentAsync();
|
|
212
|
+
|
|
213
|
+
// Use like any other agent
|
|
214
|
+
var response = await workflowAgent.RunAsync("Write a report.");
|
|
215
|
+
|
|
216
|
+
// Can be composed into larger workflows
|
|
217
|
+
Workflow outerWorkflow = AgentWorkflowBuilder.BuildSequential(workflowAgent, reviewer);
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Web API Integration
|
|
223
|
+
|
|
224
|
+
```csharp
|
|
225
|
+
// Program.cs
|
|
226
|
+
builder.AddAIAgent("Writer", (sp, key) =>
|
|
227
|
+
{
|
|
228
|
+
var chatClient = sp.GetRequiredService<IChatClient>();
|
|
229
|
+
return new ChatClientAgent(chatClient, name: key,
|
|
230
|
+
instructions: "You are a creative writing assistant.");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
builder.AddAIAgent("Editor",
|
|
234
|
+
instructions: "You improve a writer's draft.");
|
|
235
|
+
|
|
236
|
+
app.MapGet("/agent/chat", async (
|
|
237
|
+
[FromKeyedServices("Writer")] AIAgent writer,
|
|
238
|
+
[FromKeyedServices("Editor")] AIAgent editor,
|
|
239
|
+
string prompt) =>
|
|
240
|
+
{
|
|
241
|
+
Workflow workflow = AgentWorkflowBuilder.BuildSequential(writer, editor);
|
|
242
|
+
AIAgent workflowAgent = await workflow.AsAgentAsync();
|
|
243
|
+
var response = await workflowAgent.RunAsync(prompt);
|
|
244
|
+
return Results.Ok(response.Text);
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Custom Executors
|
|
251
|
+
|
|
252
|
+
For non-agent processing steps (data transformation, external API calls):
|
|
253
|
+
|
|
254
|
+
```csharp
|
|
255
|
+
public class PhotoCritiqueExecutor : BaseExecutor, IMessageHandler<WorkflowMessage>
|
|
256
|
+
{
|
|
257
|
+
public async Task HandleAsync(
|
|
258
|
+
WorkflowMessage message,
|
|
259
|
+
WorkflowContext context,
|
|
260
|
+
CancellationToken cancellationToken)
|
|
261
|
+
{
|
|
262
|
+
var client = GetVisionClient();
|
|
263
|
+
var prompt = await GetPromptAsync("critique-prompt.md");
|
|
264
|
+
|
|
265
|
+
var dataContent = new DataContent(
|
|
266
|
+
await File.ReadAllBytesAsync(message.ImagePath),
|
|
267
|
+
"image/jpeg");
|
|
268
|
+
|
|
269
|
+
var fullPrompt = new PromptBuilder()
|
|
270
|
+
.AddText(prompt)
|
|
271
|
+
.AddImage(dataContent)
|
|
272
|
+
.Build();
|
|
273
|
+
|
|
274
|
+
var response = await client.GetResponseAsync(fullPrompt);
|
|
275
|
+
await context.SendAsync(new CritiqueResult(response));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Client Types
|
|
283
|
+
|
|
284
|
+
| Type | Method | Input / Output | Use Case |
|
|
285
|
+
|------|--------|---------------|----------|
|
|
286
|
+
| Conversational | `GetConversationalClient()` | Text → Text | Chat, analysis, generation |
|
|
287
|
+
| Vision | `GetVisionClient()` | Image → Text | Image analysis, OCR |
|
|
288
|
+
| Image Generation | `GetImageClient()` | Text → Image | Mockups, visualizations |
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Prompt Engineering for Workflows
|
|
293
|
+
|
|
294
|
+
Store prompts in external markdown files for:
|
|
295
|
+
- Versioning without recompilation
|
|
296
|
+
- Independent iteration and testing
|
|
297
|
+
- Reuse across agents
|
|
298
|
+
|
|
299
|
+
```csharp
|
|
300
|
+
var prompt = await File.ReadAllTextAsync("prompts/analysis.md");
|
|
301
|
+
var agent = chatClient.AsAIAgent(instructions: prompt, name: "Analyst");
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## When to Use Which Pattern
|
|
307
|
+
|
|
308
|
+
| Scenario | Pattern | Why |
|
|
309
|
+
|----------|---------|-----|
|
|
310
|
+
| ETL / data pipeline | Sequential | Each step depends on previous |
|
|
311
|
+
| Code review (security + perf + style) | Concurrent | Independent analyses |
|
|
312
|
+
| Content creation + editing | GroupChat | Iterative refinement |
|
|
313
|
+
| Customer support routing | Handoff | Dynamic expert selection |
|
|
314
|
+
| Complex research tasks | Magentic | Adaptive planning |
|
|
315
|
+
| Composing sub-workflows | Workflow-as-Agent | Abstraction |
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Development Tools
|
|
320
|
+
|
|
321
|
+
| Tool | Purpose |
|
|
322
|
+
|------|---------|
|
|
323
|
+
| Mermaid diagrams | Auto-generated workflow visualization |
|
|
324
|
+
| Aspire Dashboard | Trace inter-agent calls and performance |
|
|
325
|
+
| `dotnet new webapi-ai` | Template: Web API with Agent Framework |
|
|
326
|
+
| `dotnet new mcp-server` | Template: MCP Server |
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Checklist
|
|
331
|
+
|
|
332
|
+
- [ ] `Microsoft.Agents.AI.Workflows` package installed
|
|
333
|
+
- [ ] Orchestration pattern selected and justified
|
|
334
|
+
- [ ] Agents have clear, single responsibilities
|
|
335
|
+
- [ ] Workflow built with `AgentWorkflowBuilder`
|
|
336
|
+
- [ ] Streaming events handled for real-time UI
|
|
337
|
+
- [ ] OpenTelemetry enabled for workflow tracing
|
|
338
|
+
- [ ] Prompts stored externally (markdown files)
|
|
339
|
+
- [ ] Error handling defined per executor
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## References
|
|
344
|
+
|
|
345
|
+
- [Workflow Orchestrations Overview](https://learn.microsoft.com/agent-framework/user-guide/workflows/orchestrations/overview)
|
|
346
|
+
- [Handoff Pattern](https://learn.microsoft.com/agent-framework/user-guide/workflows/orchestrations/handoff)
|
|
347
|
+
- [Agents in Workflows](https://learn.microsoft.com/agent-framework/tutorials/workflows/agents-in-workflows)
|
|
348
|
+
- [GitHub Samples: Workflows](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/GettingStarted/Workflows)
|
|
349
|
+
- `setup.md` — Core setup
|
|
350
|
+
- `production.md` — Middleware, A2A, MCP
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Architecture Standard: DDD Aggregates
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Aggregates are the primary unit of consistency in DDD. An aggregate root is the only entry point.
|
|
5
|
+
|
|
6
|
+
## Design Rules
|
|
7
|
+
1. **Small aggregates**: Keep aggregates small (3-5 entities max)
|
|
8
|
+
2. **Reference by ID**: Aggregates reference each other by ID only (not navigation properties across aggregate boundaries)
|
|
9
|
+
3. **Consistency boundary**: All invariants within an aggregate must hold after every command
|
|
10
|
+
4. **Single repository**: One repository per aggregate root
|
|
11
|
+
|
|
12
|
+
## Aggregate Root Pattern
|
|
13
|
+
```csharp
|
|
14
|
+
public abstract class AggregateRoot : Entity
|
|
15
|
+
{
|
|
16
|
+
private readonly List<DomainEvent> _domainEvents = [];
|
|
17
|
+
|
|
18
|
+
public IReadOnlyList<DomainEvent> DomainEvents => _domainEvents.AsReadOnly();
|
|
19
|
+
|
|
20
|
+
protected void RaiseDomainEvent(DomainEvent @event)
|
|
21
|
+
{
|
|
22
|
+
@event = @event with { Version = _domainEvents.Count + 1 };
|
|
23
|
+
_domainEvents.Add(@event);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public void ClearDomainEvents() => _domainEvents.Clear();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public abstract class Entity
|
|
30
|
+
{
|
|
31
|
+
public Guid Id { get; protected set; } = Guid.NewGuid();
|
|
32
|
+
public DateTime CreatedAt { get; protected set; } = DateTime.UtcNow;
|
|
33
|
+
public DateTime? UpdatedAt { get; protected set; }
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Order Aggregate Example
|
|
38
|
+
```csharp
|
|
39
|
+
public class Order : AggregateRoot
|
|
40
|
+
{
|
|
41
|
+
private readonly List<OrderItem> _items = [];
|
|
42
|
+
|
|
43
|
+
// Private constructor — use factory method
|
|
44
|
+
private Order(Guid userId) { UserId = userId; Status = OrderStatus.Draft; }
|
|
45
|
+
|
|
46
|
+
public Guid UserId { get; private set; }
|
|
47
|
+
public OrderStatus Status { get; private set; }
|
|
48
|
+
public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();
|
|
49
|
+
public decimal Total => _items.Sum(i => i.Subtotal);
|
|
50
|
+
|
|
51
|
+
// Factory method — validates invariants before creating
|
|
52
|
+
public static Order Create(Guid userId, IEnumerable<OrderItem> items)
|
|
53
|
+
{
|
|
54
|
+
if (userId == Guid.Empty) throw new DomainException("UserId required");
|
|
55
|
+
var order = new Order(userId);
|
|
56
|
+
foreach (var item in items) order.AddItem(item);
|
|
57
|
+
order.RaiseDomainEvent(new OrderCreatedEvent(order.Id, userId));
|
|
58
|
+
return order;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public void AddItem(OrderItem item)
|
|
62
|
+
{
|
|
63
|
+
if (Status != OrderStatus.Draft)
|
|
64
|
+
throw new DomainException($"Cannot add items to order in {Status} status");
|
|
65
|
+
|
|
66
|
+
var existing = _items.FirstOrDefault(i => i.ProductId == item.ProductId);
|
|
67
|
+
if (existing != null)
|
|
68
|
+
existing.IncreaseQuantity(item.Quantity);
|
|
69
|
+
else
|
|
70
|
+
_items.Add(item);
|
|
71
|
+
|
|
72
|
+
UpdatedAt = DateTime.UtcNow;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public void Confirm()
|
|
76
|
+
{
|
|
77
|
+
if (Status != OrderStatus.Draft)
|
|
78
|
+
throw new DomainException("Can only confirm draft orders");
|
|
79
|
+
if (!_items.Any())
|
|
80
|
+
throw new DomainException("Order must have at least one item");
|
|
81
|
+
|
|
82
|
+
Status = OrderStatus.Confirmed;
|
|
83
|
+
UpdatedAt = DateTime.UtcNow;
|
|
84
|
+
RaiseDomainEvent(new OrderConfirmedEvent(Id));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Repository Interface (per aggregate root)
|
|
90
|
+
```csharp
|
|
91
|
+
public interface IOrderRepository
|
|
92
|
+
{
|
|
93
|
+
Task<Order?> GetAsync(Guid id, CancellationToken ct = default);
|
|
94
|
+
Task AddAsync(Order order, CancellationToken ct = default);
|
|
95
|
+
Task UpdateAsync(Order order, CancellationToken ct = default);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## EF Core Configuration
|
|
100
|
+
```csharp
|
|
101
|
+
public class OrderConfiguration : IEntityTypeConfiguration<Order>
|
|
102
|
+
{
|
|
103
|
+
public void Configure(EntityTypeBuilder<Order> builder)
|
|
104
|
+
{
|
|
105
|
+
builder.HasKey(o => o.Id);
|
|
106
|
+
builder.OwnsMany(o => o.Items, items =>
|
|
107
|
+
{
|
|
108
|
+
items.ToTable("OrderItems");
|
|
109
|
+
items.WithOwner().HasForeignKey("OrderId");
|
|
110
|
+
});
|
|
111
|
+
builder.Ignore(o => o.DomainEvents); // Not persisted
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Anti-Patterns
|
|
117
|
+
- Never expose mutable collections: use `IReadOnlyList<>`, never `List<>`
|
|
118
|
+
- Never set state directly from outside: all mutations through aggregate methods
|
|
119
|
+
- Never cross aggregate boundaries in a single transaction
|
|
120
|
+
- Never put query logic in aggregates — use separate read models
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# DDD Bounded Contexts
|
|
2
|
+
|
|
3
|
+
> **Scope:** universal
|
|
4
|
+
> **Layer:** 2
|
|
5
|
+
> **Keywords:** bounded-context, ddd, domain, context-map, modular-monolith, integration-events
|
|
6
|
+
|
|
7
|
+
Bounded Context (BC) é a fronteira onde um modelo de domínio tem significado único.
|
|
8
|
+
Use apenas em sistemas com múltiplos domínios distintos (Nível 3 de complexidade).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Estrutura de Pastas (Nível 3)
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
src/{App}.Domain/
|
|
16
|
+
Billing/
|
|
17
|
+
Aggregates/
|
|
18
|
+
Subscription.cs
|
|
19
|
+
Invoice.cs
|
|
20
|
+
ValueObjects/
|
|
21
|
+
PlanTier.cs
|
|
22
|
+
BillingCycle.cs
|
|
23
|
+
Events/
|
|
24
|
+
SubscriptionCreatedEvent.cs
|
|
25
|
+
Exceptions/
|
|
26
|
+
SubscriptionNotFoundException.cs
|
|
27
|
+
|
|
28
|
+
Orders/
|
|
29
|
+
Aggregates/
|
|
30
|
+
Order.cs
|
|
31
|
+
ValueObjects/
|
|
32
|
+
Money.cs
|
|
33
|
+
Events/
|
|
34
|
+
OrderConfirmedEvent.cs
|
|
35
|
+
|
|
36
|
+
src/{App}.Application/
|
|
37
|
+
Billing/
|
|
38
|
+
Commands/
|
|
39
|
+
CreateSubscriptionCommand.cs
|
|
40
|
+
Queries/
|
|
41
|
+
GetActiveSubscriptionQuery.cs
|
|
42
|
+
Handlers/
|
|
43
|
+
CreateSubscriptionHandler.cs
|
|
44
|
+
|
|
45
|
+
src/{App}.Infrastructure/
|
|
46
|
+
Billing/
|
|
47
|
+
Repositories/
|
|
48
|
+
SubscriptionRepository.cs
|
|
49
|
+
Configurations/
|
|
50
|
+
SubscriptionConfiguration.cs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Regras de Comunicação Cross-BC
|
|
56
|
+
|
|
57
|
+
```csharp
|
|
58
|
+
// ✅ CORRETO: referenciar outro BC por ID
|
|
59
|
+
public class Order : AggregateRoot
|
|
60
|
+
{
|
|
61
|
+
public Guid CustomerId { get; private set; } // ID, não navigation property
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ✅ CORRETO: comunicação via Integration Events
|
|
65
|
+
public class OrderConfirmedHandler : INotificationHandler<OrderConfirmedIntegrationEvent>
|
|
66
|
+
{
|
|
67
|
+
public async Task Handle(OrderConfirmedIntegrationEvent notification, CancellationToken ct)
|
|
68
|
+
{
|
|
69
|
+
await _billingService.ChargeForOrderAsync(notification.OrderId, ct);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ❌ ERRADO: acesso direto ao repositório de outro BC
|
|
74
|
+
public class OrderService
|
|
75
|
+
{
|
|
76
|
+
private readonly ICustomerRepository _customerRepo; // cross-BC — PROIBIDO
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Linguagem Ubíqua por BC
|
|
83
|
+
|
|
84
|
+
O mesmo termo pode ter significados diferentes em BCs distintos:
|
|
85
|
+
|
|
86
|
+
| Termo | BC: Orders | BC: Support |
|
|
87
|
+
|-------|-----------|-------------|
|
|
88
|
+
| `Customer` | Quem fez pedido, tem `Orders[]` | Quem abriu ticket, tem `SLA` |
|
|
89
|
+
| `Status` | `Draft/Confirmed/Shipped` | `Open/InProgress/Resolved` |
|
|
90
|
+
|
|
91
|
+
Documente em `1-design/ubiquitous-language.md` por feature.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Quando NÃO usar Bounded Contexts
|
|
96
|
+
|
|
97
|
+
- App single-domain (ex: sistema de agendamentos)
|
|
98
|
+
- MVP ou projeto com um único time
|
|
99
|
+
- Quando os "domínios" compartilham o mesmo modelo sem conflito
|
|
100
|
+
|
|
101
|
+
Em todos esses casos: use Nível 2 (Aggregates sem BC isolation).
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# DDD Complexity Levels
|
|
2
|
+
|
|
3
|
+
> **Scope:** universal
|
|
4
|
+
> **Layer:** 2
|
|
5
|
+
> **Keywords:** ddd, complexity, aggregate, crud, bounded-context, domain, level
|
|
6
|
+
|
|
7
|
+
Define o nível de complexidade de domínio para cada feature. O nível determina qual
|
|
8
|
+
template de contracts.cs será usado e quais padrões DDD são mandatórios.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Os 3 Níveis
|
|
13
|
+
|
|
14
|
+
### Nível 1 — CRUD
|
|
15
|
+
|
|
16
|
+
**Quando usar:**
|
|
17
|
+
- Feature é essencialmente Create/Read/Update/Delete
|
|
18
|
+
- Entidade não tem regras de negócio (apenas validações de campos)
|
|
19
|
+
- Nenhum outro domínio precisa reagir às mudanças desta entidade
|
|
20
|
+
- Exemplos: `Category`, `Tag`, `UserProfile`, `Notification`
|
|
21
|
+
|
|
22
|
+
**O que gerar:**
|
|
23
|
+
- `Entity` simples (sem AggregateRoot)
|
|
24
|
+
- Service interface CRUD
|
|
25
|
+
- DTOs (record)
|
|
26
|
+
- Repository interface básica
|
|
27
|
+
- **SEM** Domain Events, **SEM** Value Objects obrigatórios, **SEM** CQRS
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### Nível 2 — Business Logic
|
|
32
|
+
|
|
33
|
+
**Quando usar:**
|
|
34
|
+
- Entidade tem estados com transições controladas por regras (ex: Draft → Confirmed → Shipped)
|
|
35
|
+
- Invariants existem: "só pode cancelar se estiver Ativo"
|
|
36
|
+
- Cálculos derivados (Total, Saldo, Desconto)
|
|
37
|
+
- Outros módulos precisam reagir a mudanças desta entidade
|
|
38
|
+
- Exemplos: `Order`, `Subscription`, `Invoice`, `Project`, `Ticket`
|
|
39
|
+
|
|
40
|
+
**O que gerar:**
|
|
41
|
+
- `AggregateRoot` com factory method estático
|
|
42
|
+
- Value Objects para tipos com regras (Money, Email, PhoneNumber)
|
|
43
|
+
- Domain Events para mudanças de estado relevantes
|
|
44
|
+
- Commands + Queries (CQRS com MediatR)
|
|
45
|
+
- Repository interface por aggregate root
|
|
46
|
+
- **SEM** Bounded Context isolation (módulos se comunicam diretamente)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### Nível 3 — Bounded Context
|
|
51
|
+
|
|
52
|
+
**Quando usar (opt-in explícito apenas):**
|
|
53
|
+
- Sistema tem 3+ domínios distintos onde o mesmo conceito tem modelos diferentes
|
|
54
|
+
- Exemplo: "Customer" em Billing ≠ "Customer" em Support
|
|
55
|
+
- Times distintos trabalharão em cada domínio
|
|
56
|
+
- Domínios precisam evoluir independentemente
|
|
57
|
+
- Exemplos: plataforma multi-produto, marketplace, ERP
|
|
58
|
+
|
|
59
|
+
**O que gerar:**
|
|
60
|
+
- Tudo do Nível 2 +
|
|
61
|
+
- Pasta `{BoundedContext}/` em Domain + Application
|
|
62
|
+
- Integration Events para comunicação cross-BC (via MediatR/Service Bus)
|
|
63
|
+
- Referências cross-BC por ID (nunca navigation properties)
|
|
64
|
+
- Glossário de Linguagem Ubíqua por BC
|
|
65
|
+
|
|
66
|
+
**⚠️ Não use Nível 3 por default.** Declare explicitamente na proposta.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Como Detectar o Nível
|
|
71
|
+
|
|
72
|
+
O `domain-architect` analisa a proposta e responde:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
1. A entidade principal tem estados com transições? → Sim = Nível 2+
|
|
76
|
+
2. Existem invariants de negócio? → Sim = Nível 2+
|
|
77
|
+
3. Cálculos derivados existem? → Sim = Nível 2+
|
|
78
|
+
4. Outros domínios precisam reagir a mudanças? → Sim = Nível 2+
|
|
79
|
+
5. Diferentes contextos usam modelos diferentes do mesmo conceito? → Sim = Nível 3
|
|
80
|
+
6. O usuário declarou explicitamente Bounded Context? → Sim = Nível 3
|
|
81
|
+
Se nenhuma das anteriores: Nível 1
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Template por Nível
|
|
87
|
+
|
|
88
|
+
| Nível | Template |
|
|
89
|
+
|-------|----------|
|
|
90
|
+
| 1 | `code/dotnet/contracts/contracts-level1.cs` |
|
|
91
|
+
| 2 | `code/dotnet/contracts/contracts-level2.cs` |
|
|
92
|
+
| 3 | `code/dotnet/contracts/contracts-level3.cs` |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Anti-Patterns
|
|
97
|
+
|
|
98
|
+
| Anti-Pattern | Nível Afetado | Correção |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| AggregateRoot para CRUD simples | Nível 1 desnecessário | Use Entity simples |
|
|
101
|
+
| Domain Events sem consumidores | Nível 2 excessivo | Remova ou use Nível 1 |
|
|
102
|
+
| Bounded Context para app single-domain | Nível 3 prematuro | Use Nível 2 |
|
|
103
|
+
| Entidade anêmica com regras no Service | Todos | Mova regras para o Aggregate |
|
|
104
|
+
| Value Objects para todo primitivo | Nível 2 excessivo | Só quando há regras/validação |
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
*MORPH-SPEC by Polymorphism Tech*
|