domain-add-aggregate
Adds new Domain Aggregates using Clean Architecture and DDD principles with full CRUD scaffolding across all layers
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Adds new Domain Aggregates using Clean Architecture and DDD principles with full CRUD scaffolding across all layers
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Automatically creates or updates changelogs from git commits by analyzing commit history, categorizing changes and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Inspect .NET assemblies and NuGet packages. Use when you need to get package metadata and latest version information, view public API surfaces, search for types, compare APIs between versions, or get direct access to commit-specific source (via Source Link). Works with NuGet packages, .NET platform/runtime assemblies, and local assembly files (via path). Essential for package exploration, API discovery, assembly auditing.
Write high-quality Architectural Decision Records (ADRs) following MADR format. Use when documenting important architectural decisions, technology choices, or cross-cutting concerns. Never use emoji or special characters - use plain words CORRECT for correct and WRONG for wrong.
Guide users through a structured workflow for co-authoring documentation. Use when user wants to write documentation, proposals, technical specs, decision docs, or similar structured content. This workflow helps users efficiently transfer context, refine content through iteration, and verify the doc works for readers. Trigger when user mentions writing docs, creating proposals, drafting specs, or similar documentation tasks.
Guide for authoring integration tests for minimal API endpoints using WebApplicationFactory, authenticated HttpClient, and repository-backed persistence. Use this when asked to write endpoint tests, HttpClient tests, or fixture-based integration tests.
Comprehensive guide for creating software diagrams using Mermaid syntax. Use when users need to create, visualize, or document software through diagrams including class diagrams (domain modeling, object-oriented design), sequence diagrams (application flows, API interactions, code execution), flowcharts (processes, algorithms, user journeys), entity relationship diagrams (database schemas), C4 architecture diagrams (system context, containers, components), state diagrams, git graphs, pie charts, gantt charts, or any other diagram type. Triggers include requests to "diagram", "visualize", "model", "map out", "show the flow", or when explaining system architecture, database design, code structure, or user/application flows.
| name | domain-add-aggregate |
| description | Adds new Domain Aggregates using Clean Architecture and DDD principles with full CRUD scaffolding across all layers |
This skill guides you through creating a complete Domain Aggregate with full CRUD operations following Clean Architecture, Domain-Driven Design (DDD), and bITdevKit patterns. It scaffolds code across all five layers: Domain, Infrastructure, Application, Mapping, and Presentation.
What You'll Create:
Time Estimate: 30-60 minutes for complete aggregate with CRUD operations.
Result<T> for error handlingsrc/Modules/[Module]/[Module].[Layer]/This skill assumes:
src/Modules/[Module]/
├── [Module].Domain/
│ ├── Model/
│ │ └── [Entity]Aggregate/
│ └── Events/
├── [Module].Application/
│ ├── Commands/
│ └── Queries/
├── [Module].Infrastructure/
│ └── EntityFramework/
│ └── Configurations/
├── [Module].Presentation/
│ ├── Web/
│ │ └── Endpoints/
│ └── [Module]MapperRegister.cs
Result<T>: No public constructors on aggregates[TypedEntityId<Guid>] attributeUse this skill when:
DO NOT use this skill when:
Answer these questions:
Entity Name: What is the aggregate called? (e.g., Product, Order, Invoice) Properties: What data does it hold? (Name, Description, Price, Status, etc.) Value Objects: Any complex types? (Email, Address, Money, etc.) Enumerations: Any status/type fields? (OrderStatus, ProductCategory, etc.) Business Rules: What validations? (Price > 0, Email format, Name required, etc.) Lifecycle Operations: What changes occur? (Create, Update status, Change price, etc.)
Example:
Entity: Product
Properties: Name (string), Description (string), Price (decimal), SKU (string), Status (enum)
Value Objects: Money (for Price)
Enumerations: ProductStatus (Draft, Active, Retired)
Business Rules: Name required, Price > 0, SKU unique
Operations: Create, ChangeName, ChangeDescription, ChangePrice, ChangeStatus
Based on requirements, create checklist:
[ ] Domain Layer
[ ] Create Product aggregate
[ ] Create Money value object (if needed)
[ ] Create ProductStatus enumeration
[ ] Create ProductCreatedDomainEvent
[ ] Create ProductUpdatedDomainEvent
[ ] Create ProductDeletedDomainEvent
[ ] Infrastructure Layer
[ ] Create ProductTypeConfiguration (EF Core)
[ ] Add DbSet<Product> to ModuleDbContext
[ ] Generate and apply migration
[ ] Application Layer
[ ] Create ProductCreateCommand + Handler
[ ] Create ProductUpdateCommand + Handler
[ ] Create ProductDeleteCommand + Handler
[ ] Create ProductFindOneQuery + Handler
[ ] Create ProductFindAllQuery + Handler
[ ] Mapping Layer
[ ] Create ProductModel DTO
[ ] Configure Product → ProductModel mapping
[ ] Configure value object mappings
[ ] Presentation Layer
[ ] Create ProductEndpoints (POST, GET, PUT, DELETE)
[ ] Register endpoints in module
[ ] Validation
[ ] Run build and fix errors
[ ] Test endpoints via Swagger
File: src/Modules/[Module]/[Module].Domain/Model/[Entity]Aggregate/[Entity].cs
Use template: [templates/aggregate-template.cs]
Key Patterns:
AuditableAggregateRoot<[Entity]Id>[TypedEntityId<Guid>] attributeCreate() returning Result<[Entity]>this.Change() builderExample:
[TypedEntityId<Guid>]
public class Product : AuditableAggregateRoot<ProductId>, IConcurrency
{
private Product() { }
private Product(string name, string description, decimal price, string sku)
{
this.Name = name;
this.Description = description;
this.Price = price;
this.SKU = sku;
}
public string Name { get; private set; }
public string Description { get; private set; }
public decimal Price { get; private set; }
public string SKU { get; private set; }
public ProductStatus Status { get; private set; } = ProductStatus.Draft;
public Guid ConcurrencyVersion { get; set; }
public static Result<Product> Create(string name, string description, decimal price, string sku)
{
return Result<Product>.Success()
.Ensure(_ => !string.IsNullOrWhiteSpace(name), new ValidationError("Name is required"))
.Ensure(_ => price > 0, new ValidationError("Price must be greater than zero"))
.Ensure(_ => !string.IsNullOrWhiteSpace(sku), new ValidationError("SKU is required"))
.Bind(_ => new Product(name, description, price, sku))
.Tap(e => e.DomainEvents
.Register(new ProductCreatedDomainEvent(e))
.Register(new EntityCreatedDomainEvent<Product>(e)));
}
public Result<Product> ChangeName(string name)
{
return this.Change()
.Ensure(_ => !string.IsNullOrWhiteSpace(name), "Name is required")
.Set(e => e.Name, name)
.Register(e => new ProductUpdatedDomainEvent(e))
.Apply();
}
public Result<Product> ChangePrice(decimal price)
{
return this.Change()
.Ensure(_ => price > 0, "Price must be greater than zero")
.Set(e => e.Price, price)
.Register(e => new ProductUpdatedDomainEvent(e))
.Apply();
}
public Result<Product> ChangeStatus(ProductStatus status)
{
return this.Change()
.When(_ => status != null)
.Set(e => e.Status, status)
.Register(e => new ProductUpdatedDomainEvent(e))
.Apply();
}
}
File: src/Modules/[Module]/[Module].Domain/Model/[ValueObject].cs
Use template: [templates/value-object-template.cs]
Example (EmailAddress-like pattern):
public class Money : ValueObject
{
private Money() { }
private Money(decimal amount, string currency)
{
this.Amount = amount;
this.Currency = currency;
}
public decimal Amount { get; private set; }
public string Currency { get; private set; }
public static Result<Money> Create(decimal amount, string currency)
{
return Result<Money>.Success()
.Ensure(_ => amount >= 0, new ValidationError("Amount cannot be negative"))
.Ensure(_ => !string.IsNullOrWhiteSpace(currency), new ValidationError("Currency is required"))
.Bind(_ => new Money(amount, currency));
}
protected override IEnumerable<object> GetAtomicValues()
{
yield return this.Amount;
yield return this.Currency;
}
}
When to skip: If no complex types needed, skip this step.
File: src/Modules/[Module]/[Module].Domain/Model/[Entity]Aggregate/[Entity]Status.cs
Use template: [templates/enumeration-template.cs]
Example:
public partial class ProductStatus : Enumeration
{
public static readonly ProductStatus Draft = new(1, nameof(Draft), true, "Product is in draft");
public static readonly ProductStatus Active = new(2, nameof(Active), true, "Product is active");
public static readonly ProductStatus Retired = new(3, nameof(Retired), true, "Product is retired");
public bool Enabled { get; }
public string Description { get; }
}
When to skip: If no status/type field needed, skip this step.
Files:
src/Modules/[Module]/[Module].Domain/Events/[Entity]CreatedDomainEvent.cssrc/Modules/[Module]/[Module].Domain/Events/[Entity]UpdatedDomainEvent.cssrc/Modules/[Module]/[Module].Domain/Events/[Entity]DeletedDomainEvent.csUse template: [templates/domain-events-template.cs]
Example:
public partial class ProductCreatedDomainEvent(Product model) : DomainEventBase
{
public Product Model { get; private set; } = model;
}
public partial class ProductUpdatedDomainEvent(Product model) : DomainEventBase
{
public Product Model { get; private set; } = model;
}
public partial class ProductDeletedDomainEvent(ProductId id) : DomainEventBase
{
public ProductId Id { get; private set; } = id;
}
dotnet build src/Modules/[Module]/[Module].Domain/
Expected: Build succeeds with no errors.
If errors occur:
Validate using checklist: [checklists/01-domain-layer.md]
File: src/Modules/[Module]/[Module].Infrastructure/EntityFramework/Configurations/[Entity]TypeConfiguration.cs
Use template: [templates/ef-configuration-template.cs]
Key Patterns:
IEntityTypeConfiguration<[Entity]>builder.ToTable("[Entities]")builder.HasKey(e => e.Id)builder.Property(e => e.Id).ValueGeneratedOnAdd().HasConversion(...)builder.OwnsOne(e => e.Money, ...) or builder.Property(e => e.Email).HasConversion(...)builder.Property(e => e.Status).HasConversion(...)builder.Ignore(e => e.DomainEvents)builder.Property(e => e.ConcurrencyVersion).IsConcurrencyToken()Example:
public class ProductTypeConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.ToTable("Products");
builder.HasKey(e => e.Id);
builder.Property(e => e.Id)
.ValueGeneratedOnAdd()
.HasConversion(
id => id.Value,
value => ProductId.Create(value));
builder.Property(e => e.Name).IsRequired().HasMaxLength(256);
builder.Property(e => e.Description).HasMaxLength(1024);
builder.Property(e => e.Price).HasColumnType("decimal(18,2)");
builder.Property(e => e.SKU).IsRequired().HasMaxLength(50);
builder.Property(e => e.Status)
.HasConversion(
s => s.Id,
id => Enumeration.FromId<ProductStatus>(id));
builder.Property(e => e.ConcurrencyVersion).IsConcurrencyToken();
builder.Ignore(e => e.DomainEvents);
}
}
File: src/Modules/[Module]/[Module].Infrastructure/EntityFramework/[Module]DbContext.cs
Add DbSet property:
public DbSet<Product> Products => this.Set<Product>();
# From solution root
dotnet ef migrations add Add[Entity] --project src/Modules/[Module]/[Module].Infrastructure --startup-project src/Presentation.Web.Server --context [Module]DbContext
Example:
dotnet ef migrations add AddProduct --project src/Modules/CoreModule/CoreModule.Infrastructure --startup-project src/Presentation.Web.Server --context CoreModuleDbContext
Review migration file: Verify it creates correct table and columns.
dotnet build src/Modules/[Module]/[Module].Infrastructure/
Expected: Build succeeds with no errors.
Validate using checklist: [checklists/02-infrastructure-layer.md]
File: src/Modules/[Module]/[Module].Application/Commands/[Entity]CreateCommand.cs
Use template: [templates/command-create-template.cs]
Example:
public class ProductCreateCommand : IRequest<Result<ProductModel>>
{
public string Name { get; init; }
public string Description { get; init; }
public decimal Price { get; init; }
public string SKU { get; init; }
}
public class ProductCreateCommandValidator : AbstractValidator<ProductCreateCommand>
{
public ProductCreateCommandValidator()
{
this.RuleFor(c => c.Name).NotEmpty().MaximumLength(256);
this.RuleFor(c => c.Price).GreaterThan(0);
this.RuleFor(c => c.SKU).NotEmpty().MaximumLength(50);
}
}
File: src/Modules/[Module]/[Module].Application/Commands/[Entity]CreateCommandHandler.cs
Use template: [templates/command-create-handler-template.cs]
Key Patterns:
RequestHandlerBase<TRequest, TResponse>IGenericRepository<[Entity]> and IMapper[Retry] and [Timeout] attributesResult<T> pattern throughout[Entity].Create(...)await repository.InsertAsync(...)mapper.Map<[Entity], [Entity]Model>(...)Example:
[Retry(2)]
[Timeout(30)]
public class ProductCreateCommandHandler(IGenericRepository<Product> repository, IMapper mapper)
: RequestHandlerBase<ProductCreateCommand, Result<ProductModel>>
{
public override async Task<Result<ProductModel>> Process(
ProductCreateCommand request,
CancellationToken cancellationToken)
{
var productResult = Product.Create(
request.Name,
request.Description,
request.Price,
request.SKU);
if (productResult.IsFailure)
{
return Result<ProductModel>.Failure().WithMessages(productResult.Messages);
}
var product = await repository.InsertAsync(productResult.Value, cancellationToken);
return mapper.Map<Product, ProductModel>(product);
}
}
File: src/Modules/[Module]/[Module].Application/Commands/[Entity]UpdateCommand.cs
Use template: [templates/command-update-template.cs]
Example:
public class ProductUpdateCommand : IRequest<Result<ProductModel>>
{
public Guid Id { get; init; }
public string Name { get; init; }
public string Description { get; init; }
public decimal Price { get; init; }
}
public class ProductUpdateCommandValidator : AbstractValidator<ProductUpdateCommand>
{
public ProductUpdateCommandValidator()
{
this.RuleFor(c => c.Id).NotEmpty();
this.RuleFor(c => c.Name).NotEmpty().MaximumLength(256);
this.RuleFor(c => c.Price).GreaterThan(0);
}
}
File: src/Modules/[Module]/[Module].Application/Commands/[Entity]UpdateCommandHandler.cs
Use template: [templates/command-update-handler-template.cs]
Key Patterns:
await repository.FindOneAsync(id, ...)Result.Failure() if not foundentity.ChangeName(...).ChangePrice(...)await repository.UpdateAsync(...)Example:
[Retry(2)]
[Timeout(30)]
public class ProductUpdateCommandHandler(IGenericRepository<Product> repository, IMapper mapper)
: RequestHandlerBase<ProductUpdateCommand, Result<ProductModel>>
{
public override async Task<Result<ProductModel>> Process(
ProductUpdateCommand request,
CancellationToken cancellationToken)
{
var product = await repository.FindOneAsync(
ProductId.Create(request.Id),
cancellationToken: cancellationToken);
if (product == null)
{
return Result<ProductModel>.Failure($"Product with ID {request.Id} not found");
}
var nameResult = product.ChangeName(request.Name);
if (nameResult.IsFailure)
{
return Result<ProductModel>.Failure().WithMessages(nameResult.Messages);
}
var priceResult = product.ChangePrice(request.Price);
if (priceResult.IsFailure)
{
return Result<ProductModel>.Failure().WithMessages(priceResult.Messages);
}
await repository.UpdateAsync(product, cancellationToken);
return mapper.Map<Product, ProductModel>(product);
}
}
File: src/Modules/[Module]/[Module].Application/Commands/[Entity]DeleteCommand.cs
Use template: [templates/command-delete-template.cs]
Example:
public class ProductDeleteCommand : IRequest<Result>
{
public Guid Id { get; init; }
}
public class ProductDeleteCommandValidator : AbstractValidator<ProductDeleteCommand>
{
public ProductDeleteCommandValidator()
{
this.RuleFor(c => c.Id).NotEmpty();
}
}
File: src/Modules/[Module]/[Module].Application/Commands/[Entity]DeleteCommandHandler.cs
Use template: [templates/command-delete-handler-template.cs]
Example:
[Retry(2)]
[Timeout(30)]
public class ProductDeleteCommandHandler(IGenericRepository<Product> repository)
: RequestHandlerBase<ProductDeleteCommand, Result>
{
public override async Task<Result> Process(
ProductDeleteCommand request,
CancellationToken cancellationToken)
{
var product = await repository.FindOneAsync(
ProductId.Create(request.Id),
cancellationToken: cancellationToken);
if (product == null)
{
return Result.Failure($"Product with ID {request.Id} not found");
}
product.DomainEvents.Register(new ProductDeletedDomainEvent(product.Id));
await repository.DeleteAsync(product, cancellationToken);
return Result.Success();
}
}
File: src/Modules/[Module]/[Module].Application/Queries/[Entity]FindOneQuery.cs
Use template: [templates/query-findone-template.cs]
Example:
public class ProductFindOneQuery : IRequest<Result<ProductModel>>
{
public Guid Id { get; init; }
}
public class ProductFindOneQueryValidator : AbstractValidator<ProductFindOneQuery>
{
public ProductFindOneQueryValidator()
{
this.RuleFor(q => q.Id).NotEmpty();
}
}
File: src/Modules/[Module]/[Module].Application/Queries/[Entity]FindOneQueryHandler.cs
Use template: [templates/query-findone-handler-template.cs]
Example:
[Timeout(30)]
public class ProductFindOneQueryHandler(IGenericRepository<Product> repository, IMapper mapper)
: RequestHandlerBase<ProductFindOneQuery, Result<ProductModel>>
{
public override async Task<Result<ProductModel>> Process(
ProductFindOneQuery request,
CancellationToken cancellationToken)
{
var product = await repository.FindOneAsync(
ProductId.Create(request.Id),
cancellationToken: cancellationToken);
if (product == null)
{
return Result<ProductModel>.Failure($"Product with ID {request.Id} not found");
}
return mapper.Map<Product, ProductModel>(product);
}
}
File: src/Modules/[Module]/[Module].Application/Queries/[Entity]FindAllQuery.cs
Use template: [templates/query-findall-template.cs]
Example:
public class ProductFindAllQuery : IRequest<Result<IEnumerable<ProductModel>>>
{
// Optional: Add filtering/sorting parameters
}
public class ProductFindAllQueryValidator : AbstractValidator<ProductFindAllQuery>
{
public ProductFindAllQueryValidator()
{
// No validation needed for basic find all
}
}
File: src/Modules/[Module]/[Module].Application/Queries/[Entity]FindAllQueryHandler.cs
Use template: [templates/query-findall-handler-template.cs]
Example:
[Timeout(30)]
public class ProductFindAllQueryHandler(IGenericRepository<Product> repository, IMapper mapper)
: RequestHandlerBase<ProductFindAllQuery, Result<IEnumerable<ProductModel>>>
{
public override async Task<Result<IEnumerable<ProductModel>>> Process(
ProductFindAllQuery request,
CancellationToken cancellationToken)
{
var products = await repository.FindAllAsync(cancellationToken: cancellationToken);
return Result<IEnumerable<ProductModel>>.Success(
products.Select(p => mapper.Map<Product, ProductModel>(p)));
}
}
dotnet build src/Modules/[Module]/[Module].Application/
Expected: Build succeeds with no errors.
Validate using checklist: [checklists/03-application-layer.md]
File: src/Modules/[Module]/[Module].Presentation/[Entity]Model.cs
Use template: [templates/model-template.cs]
Example:
public class ProductModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string SKU { get; set; }
public string Status { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? UpdatedDate { get; set; }
}
File: src/Modules/[Module]/[Module].Presentation/[Module]MapperRegister.cs
Use template: [templates/mapper-register-template.cs]
Add mapping configurations:
public class CoreModuleMapperRegister : IRegister
{
public void Register(TypeAdapterConfig config)
{
// Existing mappings...
// Product mappings
config.ForType<Product, ProductModel>()
.Map(dest => dest.Id, src => src.Id.Value)
.Map(dest => dest.Status, src => src.Status.Name);
// Value object conversions (if applicable)
config.NewConfig<Money, decimal>()
.MapWith(src => src.Amount);
}
}
dotnet build src/Modules/[Module]/[Module].Presentation/
Expected: Build succeeds with no errors.
Validate using checklist: [checklists/04-mapping-layer.md]
File: src/Modules/[Module]/[Module].Presentation/Web/Endpoints/[Entity]Endpoints.cs
Use template: [templates/endpoint-template.cs]
Example:
public static class ProductEndpoints
{
public static IEndpointRouteBuilder MapProductEndpoints(this IEndpointRouteBuilder app)
{
app.MapPost("api/core/products", CreateProduct)
.WithName("CreateProduct")
.WithTags("Products")
.Produces<ProductModel>(StatusCodes.Status201Created)
.Produces<ProblemDetails>(StatusCodes.Status400BadRequest);
app.MapGet("api/core/products/{id:guid}", GetProduct)
.WithName("GetProduct")
.WithTags("Products")
.Produces<ProductModel>(StatusCodes.Status200OK)
.Produces<ProblemDetails>(StatusCodes.Status404NotFound);
app.MapGet("api/core/products", GetAllProducts)
.WithName("GetAllProducts")
.WithTags("Products")
.Produces<IEnumerable<ProductModel>>(StatusCodes.Status200OK);
app.MapPut("api/core/products/{id:guid}", UpdateProduct)
.WithName("UpdateProduct")
.WithTags("Products")
.Produces<ProductModel>(StatusCodes.Status200OK)
.Produces<ProblemDetails>(StatusCodes.Status404NotFound);
app.MapDelete("api/core/products/{id:guid}", DeleteProduct)
.WithName("DeleteProduct")
.WithTags("Products")
.Produces(StatusCodes.Status204NoContent)
.Produces<ProblemDetails>(StatusCodes.Status404NotFound);
return app;
}
private static async Task<IResult> CreateProduct(
[FromServices] IRequester requester,
[FromBody] ProductCreateCommand command)
{
var result = await requester.RequestAsync(command);
return result.IsSuccess
? Results.Created($"/api/core/products/{result.Value.Id}", result.Value)
: Results.BadRequest(result.ToProblemDetails());
}
private static async Task<IResult> GetProduct(
[FromServices] IRequester requester,
Guid id)
{
var result = await requester.RequestAsync(new ProductFindOneQuery { Id = id });
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound(result.ToProblemDetails());
}
private static async Task<IResult> GetAllProducts(
[FromServices] IRequester requester)
{
var result = await requester.RequestAsync(new ProductFindAllQuery());
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.ToProblemDetails());
}
private static async Task<IResult> UpdateProduct(
[FromServices] IRequester requester,
Guid id,
[FromBody] ProductUpdateCommand command)
{
var commandWithId = command with { Id = id };
var result = await requester.RequestAsync(commandWithId);
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound(result.ToProblemDetails());
}
private static async Task<IResult> DeleteProduct(
[FromServices] IRequester requester,
Guid id)
{
var result = await requester.RequestAsync(new ProductDeleteCommand { Id = id });
return result.IsSuccess
? Results.NoContent()
: Results.NotFound(result.ToProblemDetails());
}
}
File: src/Modules/[Module]/[Module].Presentation/[Module]Module.cs
Add endpoint registration:
app.MapProductEndpoints();
dotnet build src/Modules/[Module]/[Module].Presentation/
Expected: Build succeeds with no errors.
Validate using checklist: [checklists/05-presentation-layer.md]
dotnet build
Expected: Build succeeds with no errors.
If errors occur:
dotnet build --verbosity detailed for more informationdotnet ef database update --project src/Modules/[Module]/[Module].Infrastructure --startup-project src/Presentation.Web.Server --context [Module]DbContext
Expected: Database updated with new table.
dotnet run --project src/Presentation.Web.Server
Navigate to: https://localhost:5001/swagger
Test Endpoints:
Validate using checklist: [checklists/quality-gates.md]
Congratulations! You've successfully created a complete Domain Aggregate with full CRUD operations across all five layers.
Created Files Summary (for Product example):
Domain Layer (7 files):
- Product.cs (aggregate)
- Money.cs (value object, if created)
- ProductStatus.cs (enumeration)
- ProductCreatedDomainEvent.cs
- ProductUpdatedDomainEvent.cs
- ProductDeletedDomainEvent.cs
Infrastructure Layer (2 files):
- ProductTypeConfiguration.cs
- Add[Entity] migration
Application Layer (10 files):
- ProductCreateCommand.cs + ProductCreateCommandHandler.cs
- ProductUpdateCommand.cs + ProductUpdateCommandHandler.cs
- ProductDeleteCommand.cs + ProductDeleteCommandHandler.cs
- ProductFindOneQuery.cs + ProductFindOneQueryHandler.cs
- ProductFindAllQuery.cs + ProductFindAllQueryHandler.cs
Presentation Layer (2 files):
- ProductModel.cs (DTO)
- ProductEndpoints.cs
Total: ~21 files
User: "Add a Product aggregate with Name, Description, Price, and SKU properties"
Actions:
Result: Complete Product aggregate with CRUD operations (~20 files)
User: "Add a Customer aggregate with FirstName, LastName, Email (value object), and Status"
Actions:
Result: Complete Customer aggregate with value object (~22 files)
Before considering aggregate complete:
WRONG:
CORRECT:
[Entity].cs (e.g., Product.cs)[Name].cs (e.g., EmailAddress.cs)[Entity][Type].cs (e.g., ProductStatus.cs)[Entity][Action]DomainEvent.cs (e.g., ProductCreatedDomainEvent.cs)[Entity]TypeConfiguration.cs (e.g., ProductTypeConfiguration.cs)[Entity][Action]Command.cs (e.g., ProductCreateCommand.cs)[Entity][Action]CommandHandler.cs (e.g., ProductCreateCommandHandler.cs)[Entity][Action]Query.cs (e.g., ProductFindOneQuery.cs)[Entity][Action]QueryHandler.cs (e.g., ProductFindOneQueryHandler.cs)[Entity]Model.cs (e.g., ProductModel.cs)[Entity]Endpoints.cs (e.g., ProductEndpoints.cs)AGENTS.mdsrc/Modules/CoreModule/CoreModule-README.mddocs/ADR/ (especially ADR-0001, ADR-0003, ADR-0005, ADR-0012).github/skills/domain-add-aggregate/templates/ for all code templates.github/skills/domain-add-aggregate/examples/ for complete walkthroughs.github/skills/domain-add-aggregate/checklists/ for layer-specific validation