AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Dotnet Architecture

skill-mohammed-shaker-dev-dotnet-claude-skills-architecture · by mohammed-shaker-dev

Architecture decision guidance for .NET projects. Helps choose between Clean Architecture, CQRS, microservices, and other patterns based on project requirements. Use when designing system architecture, choosing patterns, or structuring new projects.

No reviews yet
0 installs
26 views
0.0% view→install

Install

$ agentstack add skill-mohammed-shaker-dev-dotnet-claude-skills-architecture

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-mohammed-shaker-dev-dotnet-claude-skills-architecture)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
8mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

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 →
Are you the author of Dotnet Architecture? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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:

  1. What is the business domain complexity?
  2. What are the scalability requirements?
  3. What is the team size and expertise?
  4. What is the expected lifetime of the project?
  5. 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:

  1. Domain has zero external dependencies
  2. Application depends only on Domain
  3. Infrastructure implements Domain interfaces
  4. 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.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.