一键导入
clean-architecture
Use when implementing or enforcing Clean Architecture with 4-layer separation and dependency inversion.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when implementing or enforcing Clean Architecture with 4-layer separation and dependency inversion.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when writing async code, propagating CancellationTokens, or fixing async/await pitfalls.
Use when applying or enforcing C# coding style — namespaces, sealed classes, var usage, XML docs.
Use when applying modern C# idioms — records, pattern matching, primary constructors, collection expressions.
Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services.
Use when selecting or implementing design patterns in C# — factory, builder, strategy, decorator, or mediator.
| name | clean-architecture |
| description | Use when implementing or enforcing Clean Architecture with 4-layer separation and dependency inversion. |
| metadata | {"category":"architecture","agent":"dotnet-architect"} |
| when_to_use | When designing or reviewing Clean Architecture layer structure and dependency rules |
src/
{Company}.{Domain}.Domain/ # Entities, value objects, domain events, interfaces
{Company}.{Domain}.Application/ # Commands, queries, handlers, DTOs, validators
{Company}.{Domain}.Infrastructure/ # EF Core, external services, file system
{Company}.{Domain}.WebApi/ # Endpoints, middleware, DI composition root
WebApi → Infrastructure → Application → Domain
↑
WebApi → Application → Domain
Domain references nothingApplication references DomainInfrastructure references Application + DomainWebApi references all layers (composition root)// Domain/Entities/Order.cs
namespace {Company}.{Domain}.Domain.Entities;
public sealed class Order : BaseEntity, IAggregateRoot
{
public string CustomerName { get; private set; } = default!;
public OrderStatus Status { get; private set; } = OrderStatus.Draft;
public decimal Total { get; private set; }
private readonly List<OrderItem> _items = [];
public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();
private Order() { } // EF Core constructor
public static Order Create(string customerName)
{
var order = new Order { CustomerName = customerName };
order.AddDomainEvent(new OrderCreatedEvent(order.Id));
return order;
}
public void AddItem(Guid productId, int quantity, decimal price)
{
Guard.Against.NegativeOrZero(quantity);
_items.Add(new OrderItem(productId, quantity, price));
Total = _items.Sum(i => i.Quantity * i.Price);
}
}
// Domain/Interfaces/IOrderRepository.cs
public interface IOrderRepository
{
Task<Order?> FindAsync(Guid id, CancellationToken ct = default);
Task<List<Order>> ListAsync(CancellationToken ct = default);
void Add(Order order);
}
// Domain/Interfaces/IUnitOfWork.cs
public interface IUnitOfWork
{
Task<int> SaveChangesAsync(CancellationToken ct = default);
}
// Application/Orders/Commands/CreateOrder/CreateOrderCommand.cs
namespace {Company}.{Domain}.Application.Orders.Commands.CreateOrder;
public sealed record CreateOrderCommand(string CustomerName) : IRequest<Guid>;
// Application/Orders/Commands/CreateOrder/CreateOrderCommandHandler.cs
internal sealed class CreateOrderCommandHandler(
IOrderRepository repository,
IUnitOfWork unitOfWork) : IRequestHandler<CreateOrderCommand, Guid>
{
public async Task<Guid> Handle(
CreateOrderCommand request, CancellationToken ct)
{
var order = Order.Create(request.CustomerName);
repository.Add(order);
await unitOfWork.SaveChangesAsync(ct);
return order.Id;
}
}
// Application/Orders/Commands/CreateOrder/CreateOrderCommandValidator.cs
public sealed class CreateOrderCommandValidator
: AbstractValidator<CreateOrderCommand>
{
public CreateOrderCommandValidator()
{
RuleFor(x => x.CustomerName).NotEmpty().MaximumLength(200);
}
}
// Infrastructure/Persistence/AppDbContext.cs
namespace {Company}.{Domain}.Infrastructure.Persistence;
internal sealed class AppDbContext(
DbContextOptions<AppDbContext> options) : DbContext(options), IUnitOfWork
{
public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(AppDbContext).Assembly);
}
}
// Infrastructure/Persistence/Repositories/OrderRepository.cs
internal sealed class OrderRepository(AppDbContext db) : IOrderRepository
{
public async Task<Order?> FindAsync(Guid id, CancellationToken ct)
=> await db.Orders
.Include(o => o.Items)
.FirstOrDefaultAsync(o => o.Id == id, ct);
public async Task<List<Order>> ListAsync(CancellationToken ct)
=> await db.Orders.ToListAsync(ct);
public void Add(Order order) => db.Orders.Add(order);
}
// Infrastructure/DependencyInjection.cs
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(
this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
configuration.GetConnectionString("Default")));
services.AddScoped<IUnitOfWork>(sp =>
sp.GetRequiredService<AppDbContext>());
services.AddScoped<IOrderRepository, OrderRepository>();
return services;
}
}
// WebApi/Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddApplicationServices()
.AddInfrastructure(builder.Configuration);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapEndpointGroups();
app.Run();
new SqlConnection)Domain, Application, Infrastructure, WebApi/Api/PresentationIUnitOfWork interface in Domain or Application layerusing statements for EF Core or infrastructure{Company}.{Domain}.{Layer}| Question | Answer |
|---|---|
| Where do entities go? | Domain |
| Where do repository interfaces go? | Domain (per aggregate root) |
| Where do DTOs go? | Application |
| Where do MediatR handlers go? | Application |
| Where does DbContext go? | Infrastructure |
| Where does DI registration go? | Each layer has its own + WebApi composes |
| Where does validation go? | Application (FluentValidation) |