@atlashub/smartstack-cli 4.32.0 → 4.34.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/.documentation/index.html +2 -2
- package/.documentation/init.html +358 -174
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +271 -44
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/mcp-scaffolding/controller.cs.hbs +54 -128
- package/templates/project/README.md +19 -0
- package/templates/project/claude-md/api.CLAUDE.md.template +315 -0
- package/templates/project/claude-md/application.CLAUDE.md.template +181 -0
- package/templates/project/claude-md/domain.CLAUDE.md.template +125 -0
- package/templates/project/claude-md/infrastructure.CLAUDE.md.template +168 -0
- package/templates/project/claude-md/root.CLAUDE.md.template +339 -0
- package/templates/project/claude-md/web.CLAUDE.md.template +339 -0
- package/templates/skills/apex/SKILL.md +16 -10
- package/templates/skills/apex/_shared.md +1 -1
- package/templates/skills/apex/references/checks/architecture-checks.sh +154 -0
- package/templates/skills/apex/references/checks/backend-checks.sh +194 -0
- package/templates/skills/apex/references/checks/frontend-checks.sh +448 -0
- package/templates/skills/apex/references/checks/infrastructure-checks.sh +255 -0
- package/templates/skills/apex/references/checks/security-checks.sh +153 -0
- package/templates/skills/apex/references/checks/seed-checks.sh +536 -0
- package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +49 -192
- package/templates/skills/apex/references/post-checks.md +124 -2156
- package/templates/skills/apex/references/smartstack-api.md +160 -957
- package/templates/skills/apex/references/smartstack-frontend.md +134 -1022
- package/templates/skills/apex/references/smartstack-layers.md +12 -6
- package/templates/skills/apex/steps/step-00-init.md +81 -238
- package/templates/skills/apex/steps/step-03-execute.md +25 -752
- package/templates/skills/apex/steps/step-03a-layer0-domain.md +118 -0
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +91 -0
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +240 -0
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +300 -0
- package/templates/skills/apex/steps/step-03e-layer4-devdata.md +44 -0
- package/templates/skills/apex/steps/step-04-examine.md +70 -150
- package/templates/skills/application/references/frontend-i18n-and-output.md +2 -2
- package/templates/skills/application/references/frontend-route-naming.md +5 -1
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +49 -198
- package/templates/skills/application/references/frontend-verification.md +11 -11
- package/templates/skills/application/steps/step-05-frontend.md +26 -15
- package/templates/skills/application/templates-frontend.md +4 -0
- package/templates/skills/cli-app-sync/SKILL.md +2 -2
- package/templates/skills/cli-app-sync/references/comparison-map.md +1 -1
- package/templates/skills/controller/references/controller-code-templates.md +70 -67
- package/templates/skills/controller/references/mcp-scaffold-workflow.md +5 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# {{ProjectName}}.Application - Memory
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Use cases orchestration. Defines WHAT the application does. Contains business logic that doesn't belong to entities.
|
|
6
|
+
|
|
7
|
+
## Dependencies
|
|
8
|
+
|
|
9
|
+
- **{{ProjectName}}.Domain** (entities, interfaces)
|
|
10
|
+
- **MediatR** (CQRS) - add when needed
|
|
11
|
+
- **FluentValidation** - add when needed
|
|
12
|
+
- **AutoMapper** - add when needed
|
|
13
|
+
|
|
14
|
+
## Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
{{ProjectName}}.Application/
|
|
18
|
+
├── Common/
|
|
19
|
+
│ ├── Authorization/ → Permissions constants
|
|
20
|
+
│ ├── Behaviors/ → MediatR pipeline behaviors
|
|
21
|
+
│ ├── Exceptions/ → Application exceptions
|
|
22
|
+
│ ├── Interfaces/
|
|
23
|
+
│ │ ├── Identity/ → ICurrentUserService, IJwtService
|
|
24
|
+
│ │ └── Persistence/ → IExtensionsDbContext
|
|
25
|
+
│ └── Licensing/ → License validation
|
|
26
|
+
├── {Feature}/ → Feature-specific commands/queries
|
|
27
|
+
│ ├── Commands/
|
|
28
|
+
│ ├── Queries/
|
|
29
|
+
│ └── DTOs/
|
|
30
|
+
└── DependencyInjection.cs
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Patterns
|
|
34
|
+
|
|
35
|
+
### Command Template (with MediatR)
|
|
36
|
+
|
|
37
|
+
```csharp
|
|
38
|
+
namespace {{ProjectName}}.Application.{Feature}.Commands;
|
|
39
|
+
|
|
40
|
+
// Command
|
|
41
|
+
public record CreateOrderCommand(string Name, decimal Amount) : IRequest<Guid>;
|
|
42
|
+
|
|
43
|
+
// Handler
|
|
44
|
+
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, Guid>
|
|
45
|
+
{
|
|
46
|
+
private readonly IExtensionsDbContext _context;
|
|
47
|
+
|
|
48
|
+
public CreateOrderCommandHandler(IExtensionsDbContext context)
|
|
49
|
+
{
|
|
50
|
+
_context = context;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async Task<Guid> Handle(CreateOrderCommand request, CancellationToken ct)
|
|
54
|
+
{
|
|
55
|
+
var order = Order.Create(request.Name, request.Amount);
|
|
56
|
+
|
|
57
|
+
await _context.Orders.AddAsync(order, ct);
|
|
58
|
+
await _context.SaveChangesAsync(ct);
|
|
59
|
+
|
|
60
|
+
return order.Id;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Validator (FluentValidation)
|
|
65
|
+
public class CreateOrderCommandValidator : AbstractValidator<CreateOrderCommand>
|
|
66
|
+
{
|
|
67
|
+
public CreateOrderCommandValidator()
|
|
68
|
+
{
|
|
69
|
+
RuleFor(x => x.Name)
|
|
70
|
+
.NotEmpty().WithMessage("Name is required")
|
|
71
|
+
.MaximumLength(200).WithMessage("Name max 200 chars");
|
|
72
|
+
|
|
73
|
+
RuleFor(x => x.Amount)
|
|
74
|
+
.GreaterThan(0).WithMessage("Amount must be positive");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Query Template
|
|
80
|
+
|
|
81
|
+
```csharp
|
|
82
|
+
namespace {{ProjectName}}.Application.{Feature}.Queries;
|
|
83
|
+
|
|
84
|
+
// Query
|
|
85
|
+
public record GetOrdersQuery(string? SearchTerm = null) : IRequest<IReadOnlyList<OrderDto>>;
|
|
86
|
+
|
|
87
|
+
// Handler
|
|
88
|
+
public class GetOrdersQueryHandler : IRequestHandler<GetOrdersQuery, IReadOnlyList<OrderDto>>
|
|
89
|
+
{
|
|
90
|
+
private readonly IExtensionsDbContext _context;
|
|
91
|
+
private readonly IMapper _mapper;
|
|
92
|
+
|
|
93
|
+
public GetOrdersQueryHandler(IExtensionsDbContext context, IMapper mapper)
|
|
94
|
+
{
|
|
95
|
+
_context = context;
|
|
96
|
+
_mapper = mapper;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public async Task<IReadOnlyList<OrderDto>> Handle(GetOrdersQuery request, CancellationToken ct)
|
|
100
|
+
{
|
|
101
|
+
var query = _context.Orders.AsNoTracking();
|
|
102
|
+
|
|
103
|
+
if (!string.IsNullOrWhiteSpace(request.SearchTerm))
|
|
104
|
+
query = query.Where(o => o.Name.Contains(request.SearchTerm));
|
|
105
|
+
|
|
106
|
+
return await query
|
|
107
|
+
.ProjectTo<OrderDto>(_mapper.ConfigurationProvider)
|
|
108
|
+
.ToListAsync(ct);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### DTO Template
|
|
114
|
+
|
|
115
|
+
```csharp
|
|
116
|
+
namespace {{ProjectName}}.Application.{Feature}.DTOs;
|
|
117
|
+
|
|
118
|
+
public record OrderDto(Guid Id, string Name, decimal Amount, DateTime CreatedAt);
|
|
119
|
+
|
|
120
|
+
// AutoMapper Profile
|
|
121
|
+
public class OrderProfile : Profile
|
|
122
|
+
{
|
|
123
|
+
public OrderProfile()
|
|
124
|
+
{
|
|
125
|
+
CreateMap<Order, OrderDto>();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Interface Template (ExtensionsDbContext)
|
|
131
|
+
|
|
132
|
+
```csharp
|
|
133
|
+
namespace {{ProjectName}}.Application.Common.Interfaces.Persistence;
|
|
134
|
+
|
|
135
|
+
public interface IExtensionsDbContext
|
|
136
|
+
{
|
|
137
|
+
DbSet<Order> Orders { get; }
|
|
138
|
+
// Add DbSets for your entities here
|
|
139
|
+
Task<int> SaveChangesAsync(CancellationToken ct = default);
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## DependencyInjection Setup
|
|
144
|
+
|
|
145
|
+
```csharp
|
|
146
|
+
public static class DependencyInjection
|
|
147
|
+
{
|
|
148
|
+
public static IServiceCollection AddApplication(this IServiceCollection services)
|
|
149
|
+
{
|
|
150
|
+
var assembly = typeof(DependencyInjection).Assembly;
|
|
151
|
+
|
|
152
|
+
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(assembly));
|
|
153
|
+
services.AddValidatorsFromAssembly(assembly);
|
|
154
|
+
services.AddAutoMapper(assembly);
|
|
155
|
+
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
|
|
156
|
+
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
|
|
157
|
+
|
|
158
|
+
return services;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Rules
|
|
164
|
+
|
|
165
|
+
1. **NO** direct database access - use `IExtensionsDbContext` (from Common/Interfaces/Persistence)
|
|
166
|
+
2. **NO** HttpContext, Controllers - that's API layer
|
|
167
|
+
3. **Commands** modify state, return Id or nothing
|
|
168
|
+
4. **Queries** read-only, return DTOs
|
|
169
|
+
5. **Handlers** are the use cases
|
|
170
|
+
6. **One handler per command/query**
|
|
171
|
+
|
|
172
|
+
## When Adding New Feature
|
|
173
|
+
|
|
174
|
+
1. Create folder in `{Feature}/` (appropriate namespace path)
|
|
175
|
+
2. Add `Commands/` and/or `Queries/` subfolders
|
|
176
|
+
3. Add `DTOs/` subfolder if needed
|
|
177
|
+
4. Create Command/Query record
|
|
178
|
+
5. Create Handler class
|
|
179
|
+
6. Create Validator (optional but recommended)
|
|
180
|
+
7. Add DTO record and AutoMapper profile if needed
|
|
181
|
+
8. Register in `DependencyInjection.cs`
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# {{ProjectName}}.Domain - Memory
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Core business logic. **ZERO external dependencies**. This layer defines WHAT the business does, not HOW.
|
|
6
|
+
|
|
7
|
+
## Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
{{ProjectName}}.Domain/
|
|
11
|
+
├── Common/
|
|
12
|
+
│ ├── BaseEntity.cs → Base for all entities (Id, CreatedAt, UpdatedAt)
|
|
13
|
+
│ └── IAuditableEntity.cs → Audit tracking interface
|
|
14
|
+
├── Enums/ → Domain enumerations
|
|
15
|
+
├── {Feature}/ → Feature-specific entities
|
|
16
|
+
│ ├── Order.cs → Aggregate root
|
|
17
|
+
│ └── OrderItem.cs → Child entity
|
|
18
|
+
├── Exceptions/ → DomainException, custom exceptions
|
|
19
|
+
└── Interfaces/ → Repository contracts
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Rules (STRICT)
|
|
23
|
+
|
|
24
|
+
1. **NO** NuGet packages except BCL
|
|
25
|
+
2. **NO** `using` statements referencing other projects
|
|
26
|
+
3. **NO** EF Core attributes (`[Key]`, `[Required]`) - use Fluent API in Infrastructure
|
|
27
|
+
4. **NO** DTOs - entities only
|
|
28
|
+
5. **ALL** business rules live here
|
|
29
|
+
|
|
30
|
+
## Patterns
|
|
31
|
+
|
|
32
|
+
### Entity Template
|
|
33
|
+
|
|
34
|
+
```csharp
|
|
35
|
+
namespace {{ProjectName}}.Domain.{Feature};
|
|
36
|
+
|
|
37
|
+
public class Order : BaseEntity
|
|
38
|
+
{
|
|
39
|
+
public string Name { get; private set; } = null!;
|
|
40
|
+
public decimal Amount { get; private set; }
|
|
41
|
+
public bool IsActive { get; private set; }
|
|
42
|
+
|
|
43
|
+
private Order() { } // EF Core
|
|
44
|
+
|
|
45
|
+
public static Order Create(string name, decimal amount)
|
|
46
|
+
{
|
|
47
|
+
if (string.IsNullOrWhiteSpace(name))
|
|
48
|
+
throw new DomainException("Name is required");
|
|
49
|
+
if (amount <= 0)
|
|
50
|
+
throw new DomainException("Amount must be positive");
|
|
51
|
+
|
|
52
|
+
return new Order
|
|
53
|
+
{
|
|
54
|
+
Id = Guid.NewGuid(),
|
|
55
|
+
Name = name,
|
|
56
|
+
Amount = amount,
|
|
57
|
+
IsActive = true,
|
|
58
|
+
CreatedAt = DateTime.UtcNow
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public void Deactivate()
|
|
63
|
+
{
|
|
64
|
+
IsActive = false;
|
|
65
|
+
UpdatedAt = DateTime.UtcNow;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Value Object Template
|
|
71
|
+
|
|
72
|
+
```csharp
|
|
73
|
+
namespace {{ProjectName}}.Domain.Common.ValueObjects;
|
|
74
|
+
|
|
75
|
+
public record Money(decimal Amount, string Currency)
|
|
76
|
+
{
|
|
77
|
+
public static Money Create(decimal amount, string currency)
|
|
78
|
+
{
|
|
79
|
+
if (amount < 0)
|
|
80
|
+
throw new DomainException("Amount cannot be negative");
|
|
81
|
+
if (string.IsNullOrWhiteSpace(currency))
|
|
82
|
+
throw new DomainException("Currency is required");
|
|
83
|
+
|
|
84
|
+
return new Money(amount, currency);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Domain Event Template
|
|
90
|
+
|
|
91
|
+
```csharp
|
|
92
|
+
namespace {{ProjectName}}.Domain.Common.Events;
|
|
93
|
+
|
|
94
|
+
public record OrderCreatedEvent(Guid OrderId, string Name, DateTime OccurredAt);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Repository Interface Template
|
|
98
|
+
|
|
99
|
+
```csharp
|
|
100
|
+
namespace {{ProjectName}}.Domain.Interfaces;
|
|
101
|
+
|
|
102
|
+
public interface IRepository<T> where T : BaseEntity
|
|
103
|
+
{
|
|
104
|
+
Task<T?> GetByIdAsync(Guid id, CancellationToken ct = default);
|
|
105
|
+
Task<IReadOnlyList<T>> GetAllAsync(CancellationToken ct = default);
|
|
106
|
+
Task AddAsync(T entity, CancellationToken ct = default);
|
|
107
|
+
void Update(T entity);
|
|
108
|
+
void Remove(T entity);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Validation
|
|
113
|
+
|
|
114
|
+
- **Entities validate themselves** in constructors and methods
|
|
115
|
+
- Throw `DomainException` for business rule violations
|
|
116
|
+
- Use **Guard clauses** at method start
|
|
117
|
+
|
|
118
|
+
## When Adding New Entity
|
|
119
|
+
|
|
120
|
+
1. Create in the appropriate domain folder
|
|
121
|
+
2. Inherit from `BaseEntity`
|
|
122
|
+
3. Private parameterless constructor for EF
|
|
123
|
+
4. Static `Create()` factory method
|
|
124
|
+
5. Encapsulate all state changes in methods
|
|
125
|
+
6. Add repository interface in `Interfaces/`
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# {{ProjectName}}.Infrastructure - Memory
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Technical implementations. HOW things work. EF Core, external APIs, file system, email, etc.
|
|
6
|
+
|
|
7
|
+
## Dependencies
|
|
8
|
+
|
|
9
|
+
- **{{ProjectName}}.Domain** (entities, repository interfaces)
|
|
10
|
+
- **{{ProjectName}}.Application** (IExtensionsDbContext, service interfaces)
|
|
11
|
+
- **Microsoft.EntityFrameworkCore.SqlServer**
|
|
12
|
+
- **Microsoft.EntityFrameworkCore.Design**
|
|
13
|
+
|
|
14
|
+
## Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
{{ProjectName}}.Infrastructure/
|
|
18
|
+
├── Persistence/
|
|
19
|
+
│ ├── ExtensionsDbContext.cs → EF Core DbContext (implements IExtensionsDbContext)
|
|
20
|
+
│ ├── Configurations/ → Entity configurations (Fluent API)
|
|
21
|
+
│ │ ├── {Feature}/ → Feature-specific configs
|
|
22
|
+
│ │ └── SchemaConstants.cs → Extensions="extensions"
|
|
23
|
+
│ ├── Migrations/ → EF Core migrations
|
|
24
|
+
│ └── Seeding/ → Database seeders
|
|
25
|
+
├── Services/ → Service implementations
|
|
26
|
+
└── DependencyInjection.cs
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Patterns
|
|
30
|
+
|
|
31
|
+
### DbContext Template
|
|
32
|
+
|
|
33
|
+
```csharp
|
|
34
|
+
namespace {{ProjectName}}.Infrastructure.Persistence;
|
|
35
|
+
|
|
36
|
+
public class ExtensionsDbContext : DbContext, IExtensionsDbContext
|
|
37
|
+
{
|
|
38
|
+
public ExtensionsDbContext(DbContextOptions<ExtensionsDbContext> options)
|
|
39
|
+
: base(options) { }
|
|
40
|
+
|
|
41
|
+
public DbSet<Order> Orders => Set<Order>();
|
|
42
|
+
|
|
43
|
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
44
|
+
{
|
|
45
|
+
base.OnModelCreating(modelBuilder);
|
|
46
|
+
modelBuilder.HasDefaultSchema(SchemaConstants.Extensions);
|
|
47
|
+
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ExtensionsDbContext).Assembly);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public override async Task<int> SaveChangesAsync(CancellationToken ct = default)
|
|
51
|
+
{
|
|
52
|
+
return await base.SaveChangesAsync(ct);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Entity Configuration Template
|
|
58
|
+
|
|
59
|
+
```csharp
|
|
60
|
+
namespace {{ProjectName}}.Infrastructure.Persistence.Configurations.{Feature};
|
|
61
|
+
|
|
62
|
+
public class OrderConfiguration : IEntityTypeConfiguration<Order>
|
|
63
|
+
{
|
|
64
|
+
public void Configure(EntityTypeBuilder<Order> builder)
|
|
65
|
+
{
|
|
66
|
+
builder.ToTable("ext_Orders", SchemaConstants.Extensions);
|
|
67
|
+
|
|
68
|
+
builder.HasKey(o => o.Id);
|
|
69
|
+
|
|
70
|
+
builder.Property(o => o.Name)
|
|
71
|
+
.IsRequired()
|
|
72
|
+
.HasMaxLength(200);
|
|
73
|
+
|
|
74
|
+
builder.Property(o => o.Amount)
|
|
75
|
+
.HasPrecision(18, 2);
|
|
76
|
+
|
|
77
|
+
builder.HasIndex(o => o.Name);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Repository Implementation Template
|
|
83
|
+
|
|
84
|
+
```csharp
|
|
85
|
+
namespace {{ProjectName}}.Infrastructure.Persistence.Repositories;
|
|
86
|
+
|
|
87
|
+
public class OrderRepository : IOrderRepository
|
|
88
|
+
{
|
|
89
|
+
private readonly ExtensionsDbContext _context;
|
|
90
|
+
|
|
91
|
+
public OrderRepository(ExtensionsDbContext context)
|
|
92
|
+
{
|
|
93
|
+
_context = context;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public async Task<Order?> GetByIdAsync(Guid id, CancellationToken ct = default)
|
|
97
|
+
=> await _context.Orders.FindAsync(new object[] { id }, ct);
|
|
98
|
+
|
|
99
|
+
public async Task<IReadOnlyList<Order>> GetAllAsync(CancellationToken ct = default)
|
|
100
|
+
=> await _context.Orders.ToListAsync(ct);
|
|
101
|
+
|
|
102
|
+
public async Task AddAsync(Order entity, CancellationToken ct = default)
|
|
103
|
+
=> await _context.Orders.AddAsync(entity, ct);
|
|
104
|
+
|
|
105
|
+
public void Update(Order entity)
|
|
106
|
+
=> _context.Orders.Update(entity);
|
|
107
|
+
|
|
108
|
+
public void Remove(Order entity)
|
|
109
|
+
=> _context.Orders.Remove(entity);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## DependencyInjection
|
|
114
|
+
|
|
115
|
+
```csharp
|
|
116
|
+
public static class DependencyInjection
|
|
117
|
+
{
|
|
118
|
+
public static IServiceCollection AddInfrastructure(
|
|
119
|
+
this IServiceCollection services,
|
|
120
|
+
IConfiguration configuration)
|
|
121
|
+
{
|
|
122
|
+
services.AddDbContext<ExtensionsDbContext>(options =>
|
|
123
|
+
options.UseSqlServer(
|
|
124
|
+
configuration.GetConnectionString("DefaultConnection"),
|
|
125
|
+
b => b.MigrationsAssembly(typeof(ExtensionsDbContext).Assembly.FullName)));
|
|
126
|
+
|
|
127
|
+
services.AddScoped<IExtensionsDbContext>(provider =>
|
|
128
|
+
provider.GetRequiredService<ExtensionsDbContext>());
|
|
129
|
+
|
|
130
|
+
return services;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## EF Core Commands
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Create migration (ExtensionsDbContext)
|
|
139
|
+
dotnet ef migrations add <Name> --context ExtensionsDbContext -p src/{{ProjectName}}.Infrastructure -s src/{{ProjectName}}.Api -o Persistence/Migrations
|
|
140
|
+
|
|
141
|
+
# Update database
|
|
142
|
+
dotnet ef database update --context ExtensionsDbContext -p src/{{ProjectName}}.Infrastructure -s src/{{ProjectName}}.Api
|
|
143
|
+
|
|
144
|
+
# Remove last migration
|
|
145
|
+
dotnet ef migrations remove --context ExtensionsDbContext -p src/{{ProjectName}}.Infrastructure -s src/{{ProjectName}}.Api
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Rules
|
|
149
|
+
|
|
150
|
+
1. **ALL** EF configurations in `Configurations/` folder, organized by feature
|
|
151
|
+
2. **NO** data annotations on entities - use Fluent API only
|
|
152
|
+
3. **Repositories** implement interfaces from Application
|
|
153
|
+
4. **DbContext** implements `IExtensionsDbContext` from Application
|
|
154
|
+
5. **Migrations** stay in this project under `Persistence/Migrations/`
|
|
155
|
+
6. **SQL objects via SqlObjectHelper** - TVFs/views use `SqlObjectHelper.ApplyAll(migrationBuilder)` and embedded `.sql` files
|
|
156
|
+
7. **NO sequential/deterministic GUIDs in seed data** - All GUIDs must be generated via `[guid]::NewGuid()`
|
|
157
|
+
8. **Schema prefix pattern** - Table names use format `{prefix}_{EntityName}` with schema `extensions`. Use `SchemaConstants.Extensions` constant.
|
|
158
|
+
|
|
159
|
+
## When Adding New Entity
|
|
160
|
+
|
|
161
|
+
1. Create entity in `{{ProjectName}}.Domain` with factory method
|
|
162
|
+
2. Create repository interface in `{{ProjectName}}.Application/Common/Interfaces/Persistence/`
|
|
163
|
+
3. Add `DbSet<Entity>` to `ExtensionsDbContext`
|
|
164
|
+
4. Create configuration in `Persistence/Configurations/{Feature}/{Entity}Configuration.cs`
|
|
165
|
+
5. Create repository in `Persistence/Repositories/{Entity}Repository.cs` (if needed)
|
|
166
|
+
6. Register repository in `DependencyInjection.cs`
|
|
167
|
+
7. Create migration: `/efcore:migration Add{Entity}`
|
|
168
|
+
8. Run `/efcore:db-deploy` to apply migration
|