Install
$ agentstack add skill-mohammed-shaker-dev-dotnet-claude-skills-architecture ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
.NET Architecture Decision Skill
This skill helps make informed architecture decisions for .NET projects based on requirements, team size, and complexity.
Core Philosophy
> The mark of a senior architect is knowing when NOT to over-engineer. > > Simple features need simple structures. Complex systems need complex architecture. Never apply Clean Architecture to a 50-line utility.
Architecture Decision Framework
Step 1: Assess Requirements
Ask these questions:
- What is the business domain complexity?
- What are the scalability requirements?
- What is the team size and expertise?
- What is the expected lifetime of the project?
- Are there specific non-functional requirements (performance, security)?
Step 2: Choose Architecture Tier
| Complexity | Structure | Use When | |------------|-----------|----------| | Tier 1: Simple | Single project | Utilities, MCP servers, simple APIs, demos | | Tier 2: Medium | 2-3 projects | Feature-complete apps, RAG systems, real-time apps | | Tier 3: Complex | Full Clean Architecture | Enterprise systems, microservices, DDD |
Step 3: Select Patterns
Match patterns to problems:
| Problem | Pattern | When to Use | |---------|---------|-------------| | Data access abstraction | Repository | Always (unless very simple CRUD) | | Expected failure handling | Result Pattern | Complex business logic | | Complex queries | Specification | Many query variations | | Separate read/write needs | CQRS | Different read/write models | | Audit trail needed | Event Sourcing | Financial, compliance systems | | Multiple bounded contexts | Microservices | Large teams, independent deployment |
Architecture Patterns
Tier 1: Simple Structure
Use for: MCP servers, utilities, single-feature demos, CLI tools
ProjectName/
├── README.md
├── src/
│ └── ProjectName/
│ ├── Program.cs
│ ├── Services/
│ ├── Models/
│ └── appsettings.json
├── tests/
│ └── ProjectName.Tests/
├── Dockerfile
└── .github/workflows/ci.yml
Characteristics:
- Single project, no layers
- Direct service classes
- Simple DI registration
- Inline configuration
- Basic error handling
Example: MCP Server
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.MapToolEndpoints();
app.Run();
// Services/DatabaseService.cs
public sealed class DatabaseService(IDbConnection db) : IDatabaseService
{
public async Task> QueryAsync(string sql)
=> await db.QueryAsync(sql);
}
Tier 2: Medium Structure
Use for: Feature-complete applications, bounded contexts, vertical slices
ProjectName/
├── README.md
├── src/
│ ├── ProjectName.Api/ # Endpoints + DI setup
│ ├── ProjectName.Core/ # Business logic + Interfaces
│ └── ProjectName.Infrastructure/ # Data access + External services
├── tests/
│ ├── ProjectName.Core.Tests/
│ └── ProjectName.Integration.Tests/
├── docker-compose.yml
└── .github/workflows/ci.yml
Dependency Flow:
Api → Core ← Infrastructure
↑
(depends on)
Characteristics:
- 2-3 projects with clear responsibilities
- Repository pattern
- MediatR for command/query separation (optional)
- Proper configuration management
- Integration tests with Testcontainers
Example Structure:
// Core/Interfaces/IOrderRepository.cs
public interface IOrderRepository
{
Task GetByIdAsync(Guid id, CancellationToken ct);
Task AddAsync(Order order, CancellationToken ct);
}
// Core/Services/OrderService.cs
public sealed class OrderService(IOrderRepository repository)
{
public async Task GetOrderAsync(Guid id, CancellationToken ct)
=> await repository.GetByIdAsync(id, ct);
}
// Infrastructure/Repositories/OrderRepository.cs
public sealed class OrderRepository(AppDbContext db) : IOrderRepository
{
public async Task GetByIdAsync(Guid id, CancellationToken ct)
=> await db.Orders.FindAsync([id], ct);
}
// Api/Endpoints/OrderEndpoints.cs
public static class OrderEndpoints
{
public static void MapOrderEndpoints(this IEndpointRouteBuilder app)
{
app.MapGet("/orders/{id}", GetOrder);
}
}
Tier 3: Complex Structure (Clean Architecture)
Use for: Enterprise systems, microservices, DDD implementations
ProjectName/
├── README.md
├── docs/
│ ├── architecture.md
│ └── adr/
├── src/
│ ├── Domain/
│ │ ├── Entities/
│ │ ├── ValueObjects/
│ │ ├── Events/
│ │ └── Interfaces/
│ ├── Application/
│ │ ├── Commands/
│ │ ├── Queries/
│ │ ├── DTOs/
│ │ └── Validators/
│ ├── Infrastructure/
│ │ ├── Persistence/
│ │ ├── Messaging/
│ │ └── ExternalServices/
│ └── WebApi/
│ ├── Endpoints/
│ └── Middleware/
├── tests/
│ ├── Domain.Tests/
│ ├── Application.Tests/
│ └── Integration.Tests/
├── docker-compose.yml
└── .github/workflows/ci.yml
Dependency Flow:
WebApi
↓
Application
↓
Domain (no dependencies)
↑
Infrastructure (implements Domain interfaces)
Key Principles:
- Domain has zero external dependencies
- Application depends only on Domain
- Infrastructure implements Domain interfaces
- WebApi orchestrates everything
Pattern Implementations
Repository Pattern
// Domain/Interfaces/IRepository.cs
public interface IRepository where T : Entity
{
Task GetByIdAsync(Guid id, CancellationToken ct = default);
Task> GetAllAsync(CancellationToken ct = default);
Task AddAsync(T entity, CancellationToken ct = default);
Task UpdateAsync(T entity, CancellationToken ct = default);
Task DeleteAsync(T entity, CancellationToken ct = default);
}
// Domain/Interfaces/IOrderRepository.cs
public interface IOrderRepository : IRepository
{
Task> GetByCustomerIdAsync(
Guid customerId,
CancellationToken ct = default);
}
Result Pattern
// Domain/Common/Result.cs
public sealed class Result
{
public bool IsSuccess { get; }
public T? Value { get; }
public Error? Error { get; }
private Result(bool isSuccess, T? value, Error? error)
=> (IsSuccess, Value, Error) = (isSuccess, value, error);
public static Result Success(T value) => new(true, value, null);
public static Result Failure(Error error) => new(false, default, error);
public TResult Match(
Func onSuccess,
Func onFailure)
=> IsSuccess ? onSuccess(Value!) : onFailure(Error!);
}
public sealed record Error(string Code, string Message)
{
public static Error NotFound(string entity, Guid id)
=> new("NotFound", $"{entity} with ID {id} was not found");
public static Error Validation(string message)
=> new("Validation", message);
}
CQRS with MediatR
// Application/Commands/CreateOrderCommand.cs
public sealed record CreateOrderCommand(
Guid CustomerId,
List Items) : IRequest>;
public sealed class CreateOrderCommandValidator
: AbstractValidator
{
public CreateOrderCommandValidator()
{
RuleFor(x => x.CustomerId).NotEmpty();
RuleFor(x => x.Items).NotEmpty();
}
}
public sealed class CreateOrderCommandHandler(
IOrderRepository repository,
IUnitOfWork unitOfWork)
: IRequestHandler>
{
public async Task> Handle(
CreateOrderCommand request,
CancellationToken ct)
{
var order = Order.Create(request.CustomerId, request.Items);
await repository.AddAsync(order, ct);
await unitOfWork.SaveChangesAsync(ct);
return Result.Success(order.ToDto());
}
}
// Application/Queries/GetOrderQuery.cs
public sealed record GetOrderQuery(Guid OrderId) : IRequest;
public sealed class GetOrderQueryHandler(IAppDbContext db)
: IRequestHandler
{
public async Task Handle(
GetOrderQuery request,
CancellationToken ct)
{
return await db.Orders
.AsNoTracking()
.Where(o => o.Id == request.OrderId)
.Select(o => new OrderDto(o.Id, o.Total, o.Status))
.FirstOrDefaultAsync(ct);
}
}
Domain Events
// Domain/Events/IDomainEvent.cs
public interface IDomainEvent
{
DateTime OccurredAt { get; }
}
// Domain/Events/OrderCreatedEvent.cs
public sealed record OrderCreatedEvent(
Guid OrderId,
Guid CustomerId,
decimal Total) : IDomainEvent
{
public DateTime OccurredAt { get; } = DateTime.UtcNow;
}
// Domain/Common/Entity.cs
public abstract class Entity
{
private readonly List _domainEvents = [];
public IReadOnlyCollection DomainEvents
=> _domainEvents.AsReadOnly();
protected void RaiseDomainEvent(IDomainEvent domainEvent)
=> _domainEvents.Add(domainEvent);
public void ClearDomainEvents() => _domainEvents.Clear();
}
// Domain/Entities/Order.cs
public sealed class Order : Entity
{
public static Order Create(Guid customerId, List items)
{
var order = new Order
{
Id = Guid.NewGuid(),
CustomerId = customerId,
Items = items,
CreatedAt = DateTime.UtcNow
};
order.RaiseDomainEvent(new OrderCreatedEvent(
order.Id,
order.CustomerId,
order.Total));
return order;
}
}
Decision Matrix
| Factor | Tier 1 (Simple) | Tier 2 (Medium) | Tier 3 (Complex) | |--------|-----------------|-----------------|------------------| | Business logic | Minimal | Moderate | Complex | | Database needs | None/SQLite | Single DB | Multiple DBs | | External integrations | 1-2 | 3-5 | Many | | Team size | 1 | 2-4 | 5+ | | Lines of code | new Order { / simple mapping / }; }
// ✅ Direct creation for simple cases var order = new Order { Id = Guid.NewGuid(), CustomerId = request.CustomerId };
### Premature Abstraction
```csharp
// ❌ Interface for one implementation
public interface IOrderHelper { }
public class OrderHelper : IOrderHelper { }
// ✅ Make it concrete until you need abstraction
public class OrderHelper { }
Wrong Pattern for the Job
// ❌ CQRS for simple CRUD
public record GetUserByIdQuery(int Id) : IRequest;
public class GetUserByIdQueryHandler : IRequestHandler { }
// ✅ Direct repository for simple CRUD
var user = await _userRepository.GetByIdAsync(id);
Quick Reference
When to Use Each Pattern
| Pattern | Use When | Don't Use When | |---------|----------|----------------| | Repository | Need to abstract data access | Simple scripts, utilities | | Result | Expected failures in business logic | Infrastructure code | | CQRS | Different read/write models | Simple CRUD | | Event Sourcing | Audit trail required | Simple state storage | | Domain Events | Cross-aggregate communication | Within same aggregate | | Specification | Complex, reusable queries | Simple queries |
NuGet Packages by Tier
Tier 1:
- Dapper, Serilog, Swashbuckle
Tier 2 (add):
- MediatR, FluentValidation, EF Core, Testcontainers
Tier 3 (add):
- MassTransit, Marten, OpenTelemetry
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: mohammed-shaker-dev
- Source: mohammed-shaker-dev/dotnet-claude-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.