@polymorphism-tech/morph-spec 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +279 -0
- package/bin/morph-spec.js +53 -0
- package/content/.claude/commands/morph-apply.md +66 -0
- package/content/.claude/commands/morph-archive.md +79 -0
- package/content/.claude/commands/morph-costs.md +206 -0
- package/content/.claude/commands/morph-infra.md +209 -0
- package/content/.claude/commands/morph-proposal.md +60 -0
- package/content/.claude/commands/morph-status.md +71 -0
- package/content/.claude/settings.local.json +15 -0
- package/content/.claude/skills/infra/bicep-architect.md +419 -0
- package/content/.claude/skills/infra/container-specialist.md +437 -0
- package/content/.claude/skills/infra/devops-engineer.md +405 -0
- package/content/.claude/skills/integrations/asaas-financial.md +333 -0
- package/content/.claude/skills/integrations/azure-identity.md +309 -0
- package/content/.claude/skills/integrations/clerk-auth.md +290 -0
- package/content/.claude/skills/specialists/azure-architect.md +142 -0
- package/content/.claude/skills/specialists/cost-guardian.md +110 -0
- package/content/.claude/skills/specialists/ef-modeler.md +200 -0
- package/content/.claude/skills/specialists/hangfire-orchestrator.md +245 -0
- package/content/.claude/skills/specialists/ms-agent-expert.md +209 -0
- package/content/.claude/skills/specialists/po-pm-advisor.md +197 -0
- package/content/.claude/skills/specialists/standards-architect.md +78 -0
- package/content/.claude/skills/specialists/ui-ux-designer.md +325 -0
- package/content/.claude/skills/stacks/dotnet-blazor.md +352 -0
- package/content/.claude/skills/stacks/dotnet-nextjs.md +402 -0
- package/content/.claude/skills/stacks/shopify.md +445 -0
- package/content/.morph/archive/.gitkeep +25 -0
- package/content/.morph/config/agents.json +149 -0
- package/content/.morph/config/config.template.json +96 -0
- package/content/.morph/examples/api-nextjs/README.md +241 -0
- package/content/.morph/examples/api-nextjs/contracts.ts +307 -0
- package/content/.morph/examples/api-nextjs/spec.md +399 -0
- package/content/.morph/examples/api-nextjs/tasks.md +168 -0
- package/content/.morph/examples/micro-saas/README.md +125 -0
- package/content/.morph/examples/micro-saas/contracts.cs +358 -0
- package/content/.morph/examples/micro-saas/decisions.md +246 -0
- package/content/.morph/examples/micro-saas/spec.md +236 -0
- package/content/.morph/examples/micro-saas/tasks.md +150 -0
- package/content/.morph/examples/multi-agent/README.md +309 -0
- package/content/.morph/examples/multi-agent/contracts.cs +433 -0
- package/content/.morph/examples/multi-agent/spec.md +479 -0
- package/content/.morph/examples/multi-agent/tasks.md +185 -0
- package/content/.morph/features/.gitkeep +25 -0
- package/content/.morph/project.md +159 -0
- package/content/.morph/specs/.gitkeep +20 -0
- package/content/.morph/standards/architecture.md +190 -0
- package/content/.morph/standards/azure.md +184 -0
- package/content/.morph/standards/coding.md +342 -0
- package/content/.morph/templates/agent.cs +172 -0
- package/content/.morph/templates/component.razor +239 -0
- package/content/.morph/templates/contracts.cs +217 -0
- package/content/.morph/templates/decisions.md +106 -0
- package/content/.morph/templates/infra/app-insights.bicep +63 -0
- package/content/.morph/templates/infra/container-app-env.bicep +49 -0
- package/content/.morph/templates/infra/container-app.bicep +156 -0
- package/content/.morph/templates/infra/key-vault.bicep +91 -0
- package/content/.morph/templates/infra/main.bicep +155 -0
- package/content/.morph/templates/infra/parameters.dev.json +23 -0
- package/content/.morph/templates/infra/parameters.prod.json +23 -0
- package/content/.morph/templates/infra/sql-database.bicep +103 -0
- package/content/.morph/templates/infra/storage.bicep +106 -0
- package/content/.morph/templates/integrations/asaas-client.cs +387 -0
- package/content/.morph/templates/integrations/asaas-webhook.cs +351 -0
- package/content/.morph/templates/integrations/azure-identity-config.cs +288 -0
- package/content/.morph/templates/integrations/clerk-config.cs +258 -0
- package/content/.morph/templates/job.cs +171 -0
- package/content/.morph/templates/migration.cs +83 -0
- package/content/.morph/templates/proposal.md +155 -0
- package/content/.morph/templates/recap.md +105 -0
- package/content/.morph/templates/repository.cs +141 -0
- package/content/.morph/templates/saas/subscription.cs +347 -0
- package/content/.morph/templates/saas/tenant.cs +338 -0
- package/content/.morph/templates/service.cs +139 -0
- package/content/.morph/templates/spec.md +147 -0
- package/content/.morph/templates/tasks.md +235 -0
- package/content/.morph/templates/test.cs +239 -0
- package/content/CLAUDE.md +318 -0
- package/package.json +50 -0
- package/src/commands/doctor.js +132 -0
- package/src/commands/init.js +121 -0
- package/src/commands/update.js +84 -0
- package/src/utils/file-copier.js +50 -0
- package/src/utils/logger.js +32 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# .NET + Blazor Stack
|
|
2
|
+
|
|
3
|
+
Stack principal para desenvolvimento de aplicações web com .NET e Blazor Server.
|
|
4
|
+
|
|
5
|
+
## Visão Geral
|
|
6
|
+
|
|
7
|
+
| Aspecto | Tecnologia |
|
|
8
|
+
|---------|------------|
|
|
9
|
+
| **Backend** | .NET 9+ / C# 13+ |
|
|
10
|
+
| **Frontend** | Blazor Server |
|
|
11
|
+
| **Database** | Entity Framework Core 9 + Azure SQL |
|
|
12
|
+
| **Hosting** | Azure Container Apps |
|
|
13
|
+
| **Background** | Hangfire |
|
|
14
|
+
| **AI** | Microsoft Agent Framework |
|
|
15
|
+
|
|
16
|
+
## Triggers
|
|
17
|
+
|
|
18
|
+
Keywords: `blazor`, `razor`, `server-side`, `.net`, `csharp`, `dotnet`
|
|
19
|
+
|
|
20
|
+
## Estrutura de Projeto
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
src/
|
|
24
|
+
├── {App}.Domain/ # Entidades, Value Objects, Enums
|
|
25
|
+
│ ├── Entities/
|
|
26
|
+
│ ├── ValueObjects/
|
|
27
|
+
│ ├── Enums/
|
|
28
|
+
│ └── Exceptions/
|
|
29
|
+
│
|
|
30
|
+
├── {App}.Application/ # Serviços, DTOs, Interfaces
|
|
31
|
+
│ ├── Services/
|
|
32
|
+
│ ├── DTOs/
|
|
33
|
+
│ ├── Interfaces/
|
|
34
|
+
│ └── Validators/
|
|
35
|
+
│
|
|
36
|
+
├── {App}.Infrastructure/ # EF Core, External Services
|
|
37
|
+
│ ├── Data/
|
|
38
|
+
│ │ ├── AppDbContext.cs
|
|
39
|
+
│ │ ├── Configurations/
|
|
40
|
+
│ │ └── Migrations/
|
|
41
|
+
│ ├── Services/
|
|
42
|
+
│ └── Extensions/
|
|
43
|
+
│
|
|
44
|
+
├── {App}.Web/ # Blazor Server
|
|
45
|
+
│ ├── Program.cs
|
|
46
|
+
│ ├── Components/
|
|
47
|
+
│ │ ├── Layout/
|
|
48
|
+
│ │ ├── Pages/
|
|
49
|
+
│ │ └── Shared/
|
|
50
|
+
│ ├── wwwroot/
|
|
51
|
+
│ └── appsettings.json
|
|
52
|
+
│
|
|
53
|
+
└── tests/
|
|
54
|
+
├── {App}.UnitTests/
|
|
55
|
+
└── {App}.IntegrationTests/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Configuração do Projeto
|
|
59
|
+
|
|
60
|
+
### csproj principal
|
|
61
|
+
|
|
62
|
+
```xml
|
|
63
|
+
<!-- {App}.Web.csproj -->
|
|
64
|
+
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
65
|
+
<PropertyGroup>
|
|
66
|
+
<TargetFramework>net9.0</TargetFramework>
|
|
67
|
+
<Nullable>enable</Nullable>
|
|
68
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
69
|
+
</PropertyGroup>
|
|
70
|
+
|
|
71
|
+
<ItemGroup>
|
|
72
|
+
<ProjectReference Include="..\{App}.Application\{App}.Application.csproj" />
|
|
73
|
+
<ProjectReference Include="..\{App}.Infrastructure\{App}.Infrastructure.csproj" />
|
|
74
|
+
</ItemGroup>
|
|
75
|
+
</Project>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Program.cs
|
|
79
|
+
|
|
80
|
+
```csharp
|
|
81
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
82
|
+
|
|
83
|
+
// Services
|
|
84
|
+
builder.Services.AddRazorComponents()
|
|
85
|
+
.AddInteractiveServerComponents();
|
|
86
|
+
|
|
87
|
+
// Database
|
|
88
|
+
builder.Services.AddDbContext<AppDbContext>(options =>
|
|
89
|
+
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
|
90
|
+
|
|
91
|
+
// Application Services
|
|
92
|
+
builder.Services.AddScoped<IOrderService, OrderService>();
|
|
93
|
+
builder.Services.AddScoped<ICustomerService, CustomerService>();
|
|
94
|
+
|
|
95
|
+
// Hangfire
|
|
96
|
+
builder.Services.AddHangfire(config => config
|
|
97
|
+
.UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection")));
|
|
98
|
+
builder.Services.AddHangfireServer();
|
|
99
|
+
|
|
100
|
+
var app = builder.Build();
|
|
101
|
+
|
|
102
|
+
// Pipeline
|
|
103
|
+
if (!app.Environment.IsDevelopment())
|
|
104
|
+
{
|
|
105
|
+
app.UseExceptionHandler("/Error");
|
|
106
|
+
app.UseHsts();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
app.UseHttpsRedirection();
|
|
110
|
+
app.UseStaticFiles();
|
|
111
|
+
app.UseAntiforgery();
|
|
112
|
+
|
|
113
|
+
app.MapRazorComponents<App>()
|
|
114
|
+
.AddInteractiveServerRenderMode();
|
|
115
|
+
|
|
116
|
+
app.MapHangfireDashboard("/hangfire");
|
|
117
|
+
|
|
118
|
+
app.Run();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Componentes Blazor
|
|
122
|
+
|
|
123
|
+
### Padrões de Componente
|
|
124
|
+
|
|
125
|
+
```razor
|
|
126
|
+
@* Components/Pages/Orders/OrderList.razor *@
|
|
127
|
+
@page "/orders"
|
|
128
|
+
@inject IOrderService OrderService
|
|
129
|
+
@inject NavigationManager Navigation
|
|
130
|
+
|
|
131
|
+
<PageTitle>Pedidos</PageTitle>
|
|
132
|
+
|
|
133
|
+
<div class="container mx-auto p-4">
|
|
134
|
+
<div class="flex justify-between items-center mb-6">
|
|
135
|
+
<h1 class="text-2xl font-bold">Pedidos</h1>
|
|
136
|
+
<button @onclick="CreateNew" class="btn btn-primary">
|
|
137
|
+
Novo Pedido
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
@if (_orders is null)
|
|
142
|
+
{
|
|
143
|
+
<Loading />
|
|
144
|
+
}
|
|
145
|
+
else if (!_orders.Any())
|
|
146
|
+
{
|
|
147
|
+
<EmptyState Message="Nenhum pedido encontrado" />
|
|
148
|
+
}
|
|
149
|
+
else
|
|
150
|
+
{
|
|
151
|
+
<DataTable Items="_orders">
|
|
152
|
+
<HeaderTemplate>
|
|
153
|
+
<th>Número</th>
|
|
154
|
+
<th>Cliente</th>
|
|
155
|
+
<th>Total</th>
|
|
156
|
+
<th>Status</th>
|
|
157
|
+
<th>Ações</th>
|
|
158
|
+
</HeaderTemplate>
|
|
159
|
+
<RowTemplate Context="order">
|
|
160
|
+
<td>@order.OrderNumber</td>
|
|
161
|
+
<td>@order.CustomerName</td>
|
|
162
|
+
<td>@order.Total.ToString("C")</td>
|
|
163
|
+
<td><StatusBadge Status="@order.Status" /></td>
|
|
164
|
+
<td>
|
|
165
|
+
<button @onclick="() => ViewOrder(order.Id)" class="btn btn-sm">
|
|
166
|
+
Ver
|
|
167
|
+
</button>
|
|
168
|
+
</td>
|
|
169
|
+
</RowTemplate>
|
|
170
|
+
</DataTable>
|
|
171
|
+
}
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
@code {
|
|
175
|
+
private List<OrderDto>? _orders;
|
|
176
|
+
|
|
177
|
+
protected override async Task OnInitializedAsync()
|
|
178
|
+
{
|
|
179
|
+
_orders = await OrderService.GetAllAsync();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private void CreateNew() => Navigation.NavigateTo("/orders/new");
|
|
183
|
+
|
|
184
|
+
private void ViewOrder(int id) => Navigation.NavigateTo($"/orders/{id}");
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Formulários
|
|
189
|
+
|
|
190
|
+
```razor
|
|
191
|
+
@* Components/Pages/Orders/OrderForm.razor *@
|
|
192
|
+
@page "/orders/new"
|
|
193
|
+
@page "/orders/{Id:int}/edit"
|
|
194
|
+
@inject IOrderService OrderService
|
|
195
|
+
@inject NavigationManager Navigation
|
|
196
|
+
|
|
197
|
+
<PageTitle>@(_isEdit ? "Editar" : "Novo") Pedido</PageTitle>
|
|
198
|
+
|
|
199
|
+
<EditForm Model="_model" OnValidSubmit="HandleSubmit">
|
|
200
|
+
<DataAnnotationsValidator />
|
|
201
|
+
|
|
202
|
+
<div class="space-y-4">
|
|
203
|
+
<div>
|
|
204
|
+
<label for="customerId">Cliente</label>
|
|
205
|
+
<InputSelect @bind-Value="_model.CustomerId" id="customerId" class="input">
|
|
206
|
+
<option value="">Selecione...</option>
|
|
207
|
+
@foreach (var customer in _customers)
|
|
208
|
+
{
|
|
209
|
+
<option value="@customer.Id">@customer.Name</option>
|
|
210
|
+
}
|
|
211
|
+
</InputSelect>
|
|
212
|
+
<ValidationMessage For="() => _model.CustomerId" />
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<div>
|
|
216
|
+
<label for="notes">Observações</label>
|
|
217
|
+
<InputTextArea @bind-Value="_model.Notes" id="notes" class="input" />
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<div class="flex gap-2">
|
|
221
|
+
<button type="submit" class="btn btn-primary" disabled="@_isSubmitting">
|
|
222
|
+
@if (_isSubmitting)
|
|
223
|
+
{
|
|
224
|
+
<span class="loading"></span>
|
|
225
|
+
}
|
|
226
|
+
Salvar
|
|
227
|
+
</button>
|
|
228
|
+
<button type="button" @onclick="Cancel" class="btn">Cancelar</button>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
</EditForm>
|
|
232
|
+
|
|
233
|
+
@code {
|
|
234
|
+
[Parameter] public int? Id { get; set; }
|
|
235
|
+
|
|
236
|
+
private OrderFormModel _model = new();
|
|
237
|
+
private List<CustomerDto> _customers = new();
|
|
238
|
+
private bool _isEdit => Id.HasValue;
|
|
239
|
+
private bool _isSubmitting;
|
|
240
|
+
|
|
241
|
+
protected override async Task OnInitializedAsync()
|
|
242
|
+
{
|
|
243
|
+
_customers = await CustomerService.GetAllAsync();
|
|
244
|
+
|
|
245
|
+
if (_isEdit)
|
|
246
|
+
{
|
|
247
|
+
var order = await OrderService.GetByIdAsync(Id!.Value);
|
|
248
|
+
_model = new OrderFormModel
|
|
249
|
+
{
|
|
250
|
+
CustomerId = order.CustomerId,
|
|
251
|
+
Notes = order.Notes
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private async Task HandleSubmit()
|
|
257
|
+
{
|
|
258
|
+
_isSubmitting = true;
|
|
259
|
+
|
|
260
|
+
try
|
|
261
|
+
{
|
|
262
|
+
if (_isEdit)
|
|
263
|
+
await OrderService.UpdateAsync(Id!.Value, _model);
|
|
264
|
+
else
|
|
265
|
+
await OrderService.CreateAsync(_model);
|
|
266
|
+
|
|
267
|
+
Navigation.NavigateTo("/orders");
|
|
268
|
+
}
|
|
269
|
+
finally
|
|
270
|
+
{
|
|
271
|
+
_isSubmitting = false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private void Cancel() => Navigation.NavigateTo("/orders");
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Services
|
|
280
|
+
|
|
281
|
+
```csharp
|
|
282
|
+
// Application/Services/OrderService.cs
|
|
283
|
+
public interface IOrderService
|
|
284
|
+
{
|
|
285
|
+
Task<List<OrderDto>> GetAllAsync(CancellationToken ct = default);
|
|
286
|
+
Task<OrderDto> GetByIdAsync(int id, CancellationToken ct = default);
|
|
287
|
+
Task<OrderDto> CreateAsync(CreateOrderRequest request, CancellationToken ct = default);
|
|
288
|
+
Task UpdateAsync(int id, UpdateOrderRequest request, CancellationToken ct = default);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
public class OrderService : IOrderService
|
|
292
|
+
{
|
|
293
|
+
private readonly AppDbContext _context;
|
|
294
|
+
private readonly ILogger<OrderService> _logger;
|
|
295
|
+
|
|
296
|
+
public OrderService(AppDbContext context, ILogger<OrderService> logger)
|
|
297
|
+
{
|
|
298
|
+
_context = context;
|
|
299
|
+
_logger = logger;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
public async Task<List<OrderDto>> GetAllAsync(CancellationToken ct = default)
|
|
303
|
+
{
|
|
304
|
+
return await _context.Orders
|
|
305
|
+
.Include(o => o.Customer)
|
|
306
|
+
.OrderByDescending(o => o.CreatedAt)
|
|
307
|
+
.Select(o => new OrderDto
|
|
308
|
+
{
|
|
309
|
+
Id = o.Id,
|
|
310
|
+
OrderNumber = o.OrderNumber,
|
|
311
|
+
CustomerName = o.Customer.Name,
|
|
312
|
+
Total = o.Total,
|
|
313
|
+
Status = o.Status.ToString()
|
|
314
|
+
})
|
|
315
|
+
.ToListAsync(ct);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
public async Task<OrderDto> CreateAsync(CreateOrderRequest request, CancellationToken ct = default)
|
|
319
|
+
{
|
|
320
|
+
var order = Order.Create(request.CustomerId, request.Items);
|
|
321
|
+
|
|
322
|
+
_context.Orders.Add(order);
|
|
323
|
+
await _context.SaveChangesAsync(ct);
|
|
324
|
+
|
|
325
|
+
_logger.LogInformation("Order {OrderNumber} created", order.OrderNumber);
|
|
326
|
+
|
|
327
|
+
return new OrderDto { /* map */ };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Documentação de Referência
|
|
333
|
+
|
|
334
|
+
- [Blazor Documentation](https://learn.microsoft.com/en-us/aspnet/core/blazor/)
|
|
335
|
+
- [Entity Framework Core](https://learn.microsoft.com/en-us/ef/core/)
|
|
336
|
+
- [ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/)
|
|
337
|
+
- [Hangfire](https://docs.hangfire.io/)
|
|
338
|
+
|
|
339
|
+
## Checklist de Projeto
|
|
340
|
+
|
|
341
|
+
- [ ] Estrutura de camadas (Domain, Application, Infrastructure, Web)
|
|
342
|
+
- [ ] EF Core configurado com migrations
|
|
343
|
+
- [ ] Blazor Server com componentes reutilizáveis
|
|
344
|
+
- [ ] Services com injeção de dependência
|
|
345
|
+
- [ ] Logging estruturado
|
|
346
|
+
- [ ] Validação com FluentValidation ou DataAnnotations
|
|
347
|
+
- [ ] Hangfire para background jobs
|
|
348
|
+
- [ ] Dockerfile para containerização
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
*MORPH-SPEC by Polymorphism Tech*
|