Create clean-architecture-dotnet-cheatsheet.html
ยท 1 year ago
d0d5ab434b90ab4ada7e4fb25a7c5ef67e7e8b51
Parent:
5dbfddd19
1 file changed +590 โ0
- clean-architecture-dotnet-cheatsheet.html +590 โ0
Diff
--- /dev/null +++ b/clean-architecture-dotnet-cheatsheet.html @@ -0,0 +1,590 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8" /> + <meta content="width=device-width, initial-scale=1.0" name="viewport" /> + <title>Clean Architecture in .NET Web API: Comprehensive Cheatsheet</title> + <!-- SEO Meta Description --> + <meta content="A comprehensive cheatsheet for Clean Architecture in .NET Web API projects, covering core principles, layers (Domain, Application, Infrastructure, Presentation), project structure, C# examples, and best practices." name="description" /> + <!-- Keywords --> + <meta content="Clean Architecture, .NET, Web API, ASP.NET Core, Software Architecture, Domain-Driven Design, DDD, C#, Microservices, Software Design Patterns, .NET Cheatsheet, Developer Guide" name="keywords" /> + <!-- Canonical URL --> + <link href="https://cheatsheets.davidveksler.com/clean-architecture-dotnet-cheatsheet.html" rel="canonical" /> + <!-- Favicon (using an appropriate emoji for architecture/layers) --> + <link href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>๐งฑ</text></svg>" rel="icon"/> + <!-- Social Media Metadata (Open Graph) --> + <meta content="Clean Architecture in .NET Web API: Comprehensive Cheatsheet" property="og:title"/> + <meta content="Explore Clean Architecture for .NET Web API with layers, principles, C# examples, project structure, and best practices. Ideal for .NET developers and architects." property="og:description"/> + <meta content="website" property="og:type"/> + <meta content="https://cheatsheets.davidveksler.com/clean-architecture-dotnet-cheatsheet.html" property="og:url"/> + <meta content="https://cheatsheets.davidveksler.com/images/clean-architecture-dotnet-cheatsheet.png" property="og:image"/> + <meta content="Visual diagram illustrating the layers of Clean Architecture in a .NET context." property="og:image:alt"/> + <!-- Twitter Card Metadata --> + <meta content="summary_large_image" name="twitter:card"/> + <meta content="Clean Architecture in .NET Web API: Comprehensive Cheatsheet" name="twitter:title"/> + <meta content="Your go-to guide for implementing Clean Architecture in .NET Web API projects. Covers layers, code examples, best practices, and more." name="twitter:description"/> + <meta content="https://cheatsheets.davidveksler.com/images/clean-architecture-dotnet-cheatsheet.png" name="twitter:image"/> + <meta content="Illustration of Clean Architecture layers in .NET." name="twitter:image:alt"/> + <!-- Structured Data (JSON-LD) --> + <script type="application/ld+json"> + { + "@context": "https://schema.org", + "@type": "TechArticle", + "headline": "Clean Architecture in .NET Web API: Comprehensive Cheatsheet", + "description": "A comprehensive guide to Clean Architecture in .NET Web API, detailing layers, core principles, project structure, C# examples, and best practices for developers and architects.", + "image": "https://cheatsheets.davidveksler.com/images/clean-architecture-dotnet-cheatsheet.png", + "author": { + "@type": "Person", + "name": "David Veksler (AI Generated)" + }, + "publisher": { + "@type": "Organization", + "name": "David Veksler Cheatsheets", + "logo": { + "@type": "ImageObject", + "url": "https://cheatsheets.davidveksler.com/images/logo-placeholder.png" + } + }, + "datePublished": "2025-06-06", + "dateModified": "2025-06-06", + "keywords": "Clean Architecture, .NET, Web API, ASP.NET Core, C#, Software Architecture, DDD, Software Design Patterns" + } + </script> + <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/> + <link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" rel="stylesheet"/> + <style> + :root { + --bs-body-bg: #f4f6f8; /* Light gray background */ + --bs-primary: #0D47A1; /* Deep Blue for primary accent */ + --bs-primary-dark: #002171; + --bs-primary-light: #5472d3; + --card-border-color: #d1d9e0; + --card-shadow-color: rgba(0, 0, 0, 0.07); + --text-color-main: #212529; + --text-color-secondary: #495057; + --text-color-highlight: var(--bs-primary-dark); + --blueprint-grid-color: rgba(13, 71, 161, 0.05); + + /* Category Colors */ + --color-core-principles: #0D47A1; /* Primary Blue */ + --color-layers-overview: #004D40; /* Dark Teal */ + --color-domain-layer: #1E8449; /* Green */ + --color-application-layer: #6A1B9A;/* Purple */ + --color-infrastructure-layer: #A15C0D;/* Orange/Brown */ + --color-presentation-layer: #00838F; /* Teal/Cyan */ + --color-project-structure: #455A64; /* Blue Grey */ + --color-best-practices: #37474F; /* Dark Slate Grey */ + + --current-category-color: var(--bs-primary); /* Default */ + } + + body { + background-color: var(--bs-body-bg); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + color: var(--text-color-main); + } + + .page-header { + background: linear-gradient(135deg, #e3f2fd, #bbdefb); /* Light blue gradient */ + padding: 2.5rem 1.5rem; + text-align: center; + border-bottom: 2px solid var(--bs-primary-light); + margin-bottom: 2rem; + } + .page-header h1 { + color: var(--bs-primary-dark); + font-weight: 600; + font-size: 2.5rem; + } + .page-header h1 .bi { font-size: 0.9em; vertical-align: -0.05em; margin-right: 0.4em;} + .page-header .lead { color: #343a40; font-size: 1.1rem; max-width: 900px; margin: auto; } + + .schema-container { + background-color: rgba(255, 255, 255, 0.85); + border: 1px solid var(--card-border-color); + border-left: 5px solid var(--current-category-color); + border-radius: 8px; + padding: 1.5rem; + margin-bottom: 2.5rem; + box-shadow: 0 4px 12px var(--card-shadow-color); + } + + .section-title { + color: var(--current-category-color); + margin-bottom: 1.5rem; + font-weight: 600; + font-size: 1.75rem; + border-bottom: 2px solid var(--current-category-color); + padding-bottom: 0.5rem; + display: flex; + align-items: center; + } + .section-title .bi { margin-right: 0.75rem; font-size: 1.5em; } + + /* Applying category colors */ + .section-core-principles { --current-category-color: var(--color-core-principles); } + .section-layers-overview { --current-category-color: var(--color-layers-overview); } + .section-project-structure { --current-category-color: var(--color-project-structure); } + .section-best-practices { --current-category-color: var(--color-best-practices); } + + .info-card { + background: #fff; + border: 1px solid var(--card-border-color); + border-left: 4px solid var(--current-category-color); /* Uses card's specific color */ + border-radius: 6px; + box-shadow: 0 3px 8px var(--card-shadow-color); + height: 100%; + display: flex; + flex-direction: column; + margin-bottom: 1.5rem; + } + + .info-card .card-header { + color: #fff; background-color: var(--current-category-color); /* Uses card's specific color */ + font-size: 1.2rem; text-align: center; margin: 0; padding: 0.8rem 0.6rem; + font-weight: 600; display: flex; justify-content: center; align-items: center; + gap: .6rem; + border-bottom: 1px solid var(--card-border-color); + border-radius: 5px 5px 0 0; + } + .info-card .card-header .bi { font-size: 1.3em; opacity: 0.9; } + .info-card .card-body { padding: 1.25rem; flex-grow: 1; } + .info-card h5 { font-weight: 600; color: var(--text-color-highlight); margin-bottom: 0.75rem; } + .info-card p, .info-card ul, .info-card dl { font-size: 0.95rem; color: var(--text-color-secondary); line-height: 1.6; } + .info-card ul { padding-left: 1.25rem; } + .info-card li { margin-bottom: 0.5rem; } + .info-card dl dt { font-weight: 600; color: var(--text-color-main); margin-top: 0.5rem; } + .info-card dl dd { margin-left: 1rem; margin-bottom: 0.5rem; } + + + /* Card-specific colors */ + .card-core-philosophy { --current-category-color: var(--color-core-principles); } + .card-when-to-use { --current-category-color: var(--color-core-principles); } + .card-domain-layer { --current-category-color: var(--color-domain-layer); } + .card-application-layer { --current-category-color: var(--color-application-layer); } + .card-infrastructure-layer { --current-category-color: var(--color-infrastructure-layer); } + .card-presentation-layer { --current-category-color: var(--color-presentation-layer); } + .card-project-structure { --current-category-color: var(--color-project-structure); } + .card-best-practices { --current-category-color: var(--color-best-practices); } + + + .details-toggle { + font-size: 0.9rem; + padding: 0.4rem 0.8rem; color: var(--bs-primary); + border: 1px solid var(--bs-primary); background-color: transparent; + transition: background-color 0.2s ease, color 0.2s ease; + display: inline-flex; align-items: center; gap: 0.3em; + border-radius: 4px; margin-top: 1rem; + } + .details-toggle:hover { background-color: var(--bs-primary); color: white; } + .details-toggle .bi { transition: transform 0.2s ease-in-out; } + .details-toggle[aria-expanded="true"] .bi { transform: rotate(180deg); } + + .collapse-content { + font-size: 0.9rem; + border-top: 1px solid #e0e0e0; + padding-top: 1rem; margin-top: 1rem; + background-color: #fdfdff; /* Slightly off-white for contrast */ + padding: 1rem; border-radius: 4px; + } + .collapse-content h6 { font-weight: 600; color: var(--text-color-highlight); margin-top: 0.8rem; margin-bottom: 0.4rem; font-size: 1rem; } + .collapse-content ul { padding-left: 1.2rem; margin-bottom: 0.8rem; } + .collapse-content li { margin-bottom: 0.4rem; font-size: 0.9rem; } + .collapse-content code { + font-size: 0.85em; + color: #c7254e; /* Bootstrap's code color */ + background-color: #f9f2f4; /* Bootstrap's code background */ + padding: 0.2em 0.4em; + border-radius: 3px; + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + } + .collapse-content pre { + background-color: #282c34; /* Dark background for code blocks */ + color: #abb2bf; /* Light text color for dark background */ + padding: 1em; + border-radius: 6px; + overflow-x: auto; + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 0.875em; + line-height: 1.45; + } + .collapse-content pre code { + background-color: transparent; + color: inherit; + padding: 0; + border-radius: 0; + } + + .term { + font-weight: 600; color: var(--bs-primary-dark); + background-color: var(--bs-primary-light); + padding: 0.15em 0.4em; border-radius: 4px; cursor: help; + border: 1px solid var(--bs-primary); + } + a { color: var(--bs-primary); text-decoration: none; } + a:hover { color: var(--bs-primary-dark); text-decoration: underline; } + + footer { + text-align: center; + padding: 2rem 0; + margin-top: 2rem; + background-color: #e9ecef; + color: var(--text-color-secondary); + font-size: 0.9rem; + border-top: 1px solid var(--card-border-color); + } + footer a { color: var(--bs-primary); } + footer a:hover { color: var(--bs-primary-dark); } + + @media print { + body { font-size: 10pt; background-color: #fff; } + .page-header, footer, .details-toggle { display: none; } + .schema-container { border: 1px solid #ccc; box-shadow: none; margin-bottom: 1.5rem; padding: 1rem; border-left-width: 3px; } + .section-title { font-size: 1.4rem; margin-bottom: 1rem; color: #000 !important; border-bottom-color: #000 !important;} + .info-card { box-shadow: none; border: 1px solid #ddd; border-left-width: 3px; margin-bottom: 1rem; page-break-inside: avoid; } + .info-card .card-header { background-color: #eee !important; color: #000 !important; font-size: 1.1rem; } + .collapse.show { display: block !important; } /* Ensure collapsed content is visible */ + .collapse-content { border-top: 1px solid #eee; background-color: #fff; } + pre, pre code { background-color: #f8f9fa !important; color: #212529 !important; border: 1px solid #eee; white-space: pre-wrap; word-break: break-all; } + .term { background-color: #e9ecef; border: 1px solid #ced4da; color: #000; } + a { text-decoration: none; color: #000; } + a[href^="http"]:after { content: " (" attr(href) ")"; font-size: 0.9em; } + } + </style> +</head> +<body> + <header class="page-header"> + <h1><i class="bi bi-diagram-3-fill"></i> Clean Architecture in .NET Web API</h1> + <p class="lead">A comprehensive cheatsheet covering core principles, layer responsibilities, project structure, C# examples, and best practices for building robust and maintainable .NET Web APIs using Clean Architecture.</p> + <p class="text-muted small">Last Updated: <span id="lastUpdatedDate">June 6, 2025</span></p> + </header> + + <main class="container"> + <!-- Core Philosophy & When to Use --> + <div class="schema-container section-core-principles"> + <h2 class="section-title"><i class="bi bi-journal-richtext"></i> Core Concepts</h2> + <div class="row"> + <div class="col-lg-6"> + <div class="info-card card-core-philosophy"> + <div class="card-header"><i class="bi bi-key-fill"></i> Core Philosophy</div> + <div class="card-body"> + <ul> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Source code dependencies must only point inwards. Inner layers (e.g., Domain) should not know anything about outer layers (e.g., Infrastructure, Presentation).">The Dependency Rule</span>:</strong> The cornerstone. Promotes independence of core business logic.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Inner layers define interfaces that outer layers implement. This allows specific implementations (e.g., database type) to change without affecting core logic.">Abstraction</span>:</strong> Inner layers define interfaces, outer layers implement them.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Business rules can be tested in isolation, without dependencies on UI, databases, or external services, leading to more reliable and faster tests.">Testability</span>:</strong> Business logic can be tested in isolation.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Changes in one part of the system have minimal impact on other parts, making the system easier to evolve.">Maintainability & Flexibility</span>:</strong> Easier to evolve the system with minimal impact across layers.</li> + </ul> + </div> + </div> + </div> + <div class="col-lg-6"> + <div class="info-card card-when-to-use"> + <div class="card-header"><i class="bi bi-question-circle-fill"></i> When to Use Clean Architecture</div> + <div class="card-body"> + <ul> + <li><strong>Complex Business Logic:</strong> When core rules are intricate and form the primary value.</li> + <li><strong>Long-Term Projects:</strong> For applications expected to evolve and be maintained.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="An approach to software development that centers the development on programming a domain model that has a rich understanding of the processes and rules of a domain.">Domain-Driven Design (DDD)</span> Alignment:</strong> When focusing on the domain model.</li> + <li><strong>High Testability Requirements:</strong> When isolated testing of business logic is crucial.</li> + <li><strong>Technology Independence:</strong> To defer or easily change decisions about frameworks, databases, or UI.</li> + </ul> + </div> + </div> + </div> + </div> + </div> + + <!-- Layers Overview & Responsibilities --> + <div class="schema-container section-layers-overview"> + <h2 class="section-title"><i class="bi bi-stack"></i> Layers Overview & Responsibilities</h2> + <div class="row"> + <!-- Domain Layer --> + <div class="col-md-6 col-lg-6"> + <div class="info-card card-domain-layer"> + <div class="card-header"><i class="bi bi-shield-shaded"></i> Domain Layer (Core)</div> + <div class="card-body"> + <p><strong>Purpose:</strong> Contains enterprise-wide business logic and types. Innermost layer with no dependencies on other solution layers.</p> + <button class="btn btn-sm details-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#detailsDomainLayer" aria-expanded="false" aria-controls="detailsDomainLayer"> + Key Components <i class="bi bi-chevron-down"></i> + </button> + <div class="collapse collapse-content" id="detailsDomainLayer"> + <h6>Key Components:</h6> + <ul> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Core business objects with an identity, encapsulating business logic and data (e.g., Order, Product).">Entities</span>:</strong> Core business objects with identity and logic. + <pre><code class="language-csharp">// Domain/Entities/Product.cs +public class Product +{ + public Guid Id { get; private set; } + public string Name { get; private set; } + public decimal Price { get; private set; } + + // Constructor and methods with business logic... + public Product(Guid id, string name, decimal price) { /* ... validation ... */ } + public void UpdatePrice(decimal newPrice) { /* ... logic ... */ } +}</code></pre> + </li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Immutable objects representing descriptive aspects of the domain (e.g., Address, Money), identified by their attributes.">Value Objects</span>:</strong> Immutable descriptive aspects (e.g., Address).</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="A cluster of domain objects (entities, value objects) treated as a single unit, with an aggregate root entity.">Aggregates</span>:</strong> Clusters of domain objects treated as a unit.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Operations that don't naturally belong to a single entity, often involving multiple entities.">Domain Services</span>:</strong> Logic not fitting a single entity.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Abstractions for data persistence, defined in Domain but implemented in Infrastructure (e.g., IProductRepository).">Repository Interfaces</span>:</strong> Data access abstractions. + <pre><code class="language-csharp">// Domain/Interfaces/IProductRepository.cs +public interface IProductRepository +{ + Task<Product?> GetByIdAsync(Guid id); + Task AddAsync(Product product); + // ... other methods +}</code></pre> + </li> + <li><strong>Domain Events:</strong> Represent significant occurrences.</li> + <li><strong>Custom Domain Exceptions:</strong> Signal domain rule violations.</li> + </ul> + </div> + </div> + </div> + </div> + + <!-- Application Layer --> + <div class="col-md-6 col-lg-6"> + <div class="info-card card-application-layer"> + <div class="card-header"><i class="bi bi-arrows-move"></i> Application Layer</div> + <div class="card-body"> + <p><strong>Purpose:</strong> Contains application-specific business logic. Orchestrates use cases, interacting with Domain and Infrastructure (via interfaces).</p> + <button class="btn btn-sm details-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#detailsApplicationLayer" aria-expanded="false" aria-controls="detailsApplicationLayer"> + Key Components <i class="bi bi-chevron-down"></i> + </button> + <div class="collapse collapse-content" id="detailsApplicationLayer"> + <h6>Key Components:</h6> + <ul> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Classes that implement specific application operations, often as CQRS Commands and Queries (e.g., CreateProductCommandHandler).">Use Cases/Interactors/Services</span>:</strong> Application operations. + <pre><code class="language-csharp">// Application/Products/Commands/CreateProductCommand.cs +public record CreateProductCommand(string Name, decimal Price) : IRequest<Guid>; + +// Application/Products/Handlers/CreateProductCommandHandler.cs +public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, Guid> +{ + private readonly IProductRepository _productRepository; + // ... + public async Task<Guid> Handle(CreateProductCommand request, CancellationToken ct) + { + var product = new Product(Guid.NewGuid(), request.Name, request.Price); + await _productRepository.AddAsync(product); + // await _unitOfWork.SaveChangesAsync(ct); + return product.Id; + } +}</code></pre> + </li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Simple objects used to transfer data between layers, especially Application and Presentation. Avoid exposing domain entities directly.">Data Transfer Objects (DTOs)</span>:</strong> Data carriers between layers.</li> + <li><strong>Interfaces for Infrastructure Services:</strong> Abstractions for external concerns (e.g., `IEmailSender`).</li> + <li><strong>Validation Logic:</strong> Input data validation (e.g., using <span class="term" data-bs-toggle="tooltip" title="A popular .NET library for creating strongly-typed validation rules.">FluentValidation</span>).</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Command Query Responsibility Segregation: A pattern that separates read (Query) and update (Command) operations for a data store.">CQRS</span>:</strong> Separates read and write operations. Often uses <span class="term" data-bs-toggle="tooltip" title="A popular in-process messaging library in .NET that helps implement Mediator and CQRS patterns.">MediatR</span>.</li> + <li><strong>Application Exceptions:</strong> For application-specific errors.</li> + </ul> + </div> + </div> + </div> + </div> + + <!-- Infrastructure Layer --> + <div class="col-md-6 col-lg-6"> + <div class="info-card card-infrastructure-layer"> + <div class="card-header"><i class="bi bi-hdd-network-fill"></i> Infrastructure Layer</div> + <div class="card-body"> + <p><strong>Purpose:</strong> Handles all external concerns and technical details (databases, file systems, external APIs). Implements interfaces from Application/Domain.</p> + <button class="btn btn-sm details-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#detailsInfrastructureLayer" aria-expanded="false" aria-controls="detailsInfrastructureLayer"> + Key Components <i class="bi bi-chevron-down"></i> + </button> + <div class="collapse collapse-content" id="detailsInfrastructureLayer"> + <h6>Key Components:</h6> + <ul> + <li><strong>Data Access/Persistence:</strong> Repository implementations (e.g., using EF Core). Includes `DbContext`. + <pre><code class="language-csharp">// Infrastructure/Persistence/Repositories/ProductRepository.cs +public class ProductRepository : IProductRepository +{ + private readonly ApplicationDbContext _dbContext; + public ProductRepository(ApplicationDbContext dbContext) {/*...*/} + public async Task AddAsync(Product product) {/*...*/} + // ... other implementations +}</code></pre> + </li> + <li><strong>External Service Integrations:</strong> Clients for APIs, payment gateways, email.</li> + <li><strong>Caching Implementations:</strong> (e.g., Redis).</li> + <li><strong>Identity Services:</strong> Authentication/Authorization implementations.</li> + </ul> + </div> + </div> + </div> + </div> + + <!-- Presentation Layer --> + <div class="col-md-6 col-lg-6"> + <div class="info-card card-presentation-layer"> + <div class="card-header"><i class="bi bi-display-fill"></i> Presentation Layer (Web API)</div> + <div class="card-body"> + <p><strong>Purpose:</strong> Handles user interaction (HTTP requests/responses for an API). Translates input to Application layer and presents results.</p> + <button class="btn btn-sm details-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#detailsPresentationLayer" aria-expanded="false" aria-controls="detailsPresentationLayer"> + Key Components <i class="bi bi-chevron-down"></i> + </button> + <div class="collapse collapse-content" id="detailsPresentationLayer"> + <h6>Key Components:</h6> + <ul> + <li><strong>Controllers/API Endpoints:</strong> Receive HTTP requests, delegate to Application layer. + <pre><code class="language-csharp">// Presentation/Controllers/ProductsController.cs +[ApiController] +[Route("api/[controller]")] +public class ProductsController : ControllerBase +{ + private readonly ISender _mediator; // MediatR + public ProductsController(ISender mediator) {/*...*/} + + [HttpPost] + public async Task<IActionResult> CreateProduct([FromBody] CreateProductCommand command) + { + var productId = await _mediator.Send(command); + return CreatedAtAction(nameof(GetProductById), new { id = productId }, new { id = productId }); + } + // ... other endpoints +}</code></pre> + </li> + <li><strong>Middleware:</strong> Global error handling, authentication, logging.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="The central place in an application where dependencies are registered and configured, typically in Program.cs or Startup.cs.">Dependency Injection (DI) Setup</span>:</strong> Configuration of services (<span class="term" data-bs-toggle="tooltip" title="The single place in an application where all components are wired together.">Composition Root</span>).</li> + <li><strong>API Models/ViewModels:</strong> Models for API requests/responses.</li> + </ul> + </div> + </div> + </div> + </div> + </div> + </div> + + <!-- Project Structure Example --> + <div class="schema-container section-project-structure"> + <h2 class="section-title"><i class="bi bi-folder2-open"></i> Project Structure Example</h2> + <div class="info-card card-project-structure"> + <div class="card-header"><i class="bi bi-diagram-2"></i> Solution Structure</div> + <div class="card-body"> + <pre><code class="language-text"> +Solution.sln +โโโ src +โ โโโ Domain/ (Core.csproj - .NET Class Library) +โ โ โโโ Entities/ +โ โ โโโ Interfaces/ (e.g., IRepository.cs) +โ โ โโโ ... (ValueObjects, Enums, Exceptions, Events) +โ โ +โ โโโ Application/ (Application.csproj - .NET Class Library) +โ โ โโโ Features/ (e.g., Products, Orders) +โ โ โ โโโ Products/ +โ โ โ โ โโโ Commands/ +โ โ โ โ โโโ Queries/ +โ โ โ โ โโโ DTOs/ +โ โ โโโ Common/ +โ โ โ โโโ Interfaces/ (e.g., IEmailSender.cs) +โ โ โ โโโ ... (Mappings, Behaviors) +โ โ +โ โโโ Infrastructure/ (Infrastructure.csproj - .NET Class Library) +โ โ โโโ Persistence/ +โ โ โ โโโ DataContext/ (e.g., ApplicationDbContext.cs) +โ โ โ โโโ Repositories/ (e.g., ProductRepository.cs) +โ โ โโโ Services/ (e.g., EmailSender.cs) +โ โ +โ โโโ Presentation/ (WebApi.csproj - ASP.NET Core Web API Project) +โ โโโ Controllers/ +โ โโโ Middleware/ +โ โโโ Program.cs (DI Setup) +โ +โโโ tests + โโโ Domain.UnitTests/ + โโโ Application.UnitTests/ + โโโ ... (Infrastructure.IntegrationTests, Presentation.IntegrationTests) + </code></pre> + </div> + </div> + </div> + + <!-- Best Practices & Considerations --> + <div class="schema-container section-best-practices"> + <h2 class="section-title"><i class="bi bi-check2-circle"></i> Best Practices & Considerations</h2> + <div class="info-card card-best-practices"> + <div class="card-header"><i class="bi bi-lightbulb-fill"></i> Key Practices</div> + <div class="card-body"> + <ul> + <li><strong><span class="term" data-bs-toggle="tooltip" title="A design pattern where objects receive their dependencies from an external source rather than creating them internally. Crucial for Clean Architecture.">Dependency Injection (DI)</span>:</strong> Absolutely crucial. Register dependencies in Presentation layer's `Program.cs`.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="A popular library for in-process messaging that helps implement CQRS and Mediator patterns, decoupling senders from handlers.">MediatR</span>:</strong> Widely used for CQRS in Application layer.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="A .NET library for creating strongly-typed validation rules, often used in Application layer for command/query validation.">FluentValidation</span>:</strong> Robust validation in Application layer.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="A library for object-to-object mapping, useful for converting between Entities, DTOs, and API Models.">AutoMapper</span> (or similar):</strong> For mapping between Entities, DTOs, API Models.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="A pattern that groups multiple repository operations into a single transaction, often implemented within the DbContext in the Infrastructure layer.">Unit of Work (UoW) Pattern</span>:</strong> Often in Infrastructure (e.g., `DbContext`). `IUnitOfWork` interface in Application.</li> + <li><strong>Error Handling:</strong> Global error handling middleware in Presentation. Custom exceptions in Domain/Application.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="A pattern in .NET for managing strongly-typed configuration settings, typically loaded from appsettings.json or environment variables.">Configuration (Options Pattern)</span>:</strong> Manage configuration via `IOptions<T>`.</li> + <li><strong>Async/Await:</strong> Use thoroughly for I/O-bound operations.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="A design principle stating that a class should have only one reason to change, meaning it should have only one job or responsibility.">Single Responsibility Principle (SRP)</span>:</strong> Apply to classes and methods.</li> + <li><strong>Lean Controllers:</strong> Controllers should be thin, delegating to Application layer.</li> + <li><strong>Avoid Leaking Abstractions:</strong> Don't expose `IQueryable` from repositories to outer layers.</li> + </ul> + </div> + </div> + </div> + + </main> + + <footer> + <p>© <span id="currentYear"></span> David Veksler Cheatsheets. Content primarily AI-generated for illustrative purposes.</p> + <div> + <a class="mx-2" href="https://www.linkedin.com/in/davidveksler/" target="_blank" title="David Veksler on LinkedIn"> + <i class="bi bi-linkedin"></i> LinkedIn + </a> + <a class="mx-2" href="https://cheatsheets.davidveksler.com/" title="Browse All Cheatsheets"> + <i class="bi bi-collection"></i> All Cheatsheets + </a> + </div> + </footer> + + <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> + <script> + document.addEventListener('DOMContentLoaded', function () { + // Set current year in footer + const currentYearSpan = document.getElementById('currentYear'); + if (currentYearSpan) { + currentYearSpan.textContent = new Date().getFullYear(); + } + // Set last updated date + const lastUpdatedDateSpan = document.getElementById('lastUpdatedDate'); + if (lastUpdatedDateSpan) { + // Format today's date as Month Day, Year + const today = new Date(); + const options = { year: 'numeric', month: 'long', day: 'numeric' }; + lastUpdatedDateSpan.textContent = today.toLocaleDateString('en-US', options); + } + + + // Initialize tooltips + var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); + var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl, { + boundary: document.body, // Attempt to make tooltips less likely to be cut off + html: true // Allow HTML in tooltips if needed + }); + }); + + // Handle collapse icon changes + const collapseElements = document.querySelectorAll('.collapse'); + collapseElements.forEach(collapseEl => { + const button = document.querySelector(`.details-toggle[data-bs-target="#${collapseEl.id}"]`); + if (button) { + const iconEl = button.querySelector('.bi'); + const updateIconState = () => { + if (collapseEl.classList.contains('show')) { + iconEl.classList.remove('bi-chevron-down'); + iconEl.classList.add('bi-chevron-up'); + } else { + iconEl.classList.remove('bi-chevron-up'); + iconEl.classList.add('bi-chevron-down'); + } + }; + updateIconState(); // Initial state + collapseEl.addEventListener('show.bs.collapse', updateIconState); + collapseEl.addEventListener('hide.bs.collapse', updateIconState); + } + }); + }); + </script> +</body> +</html> \ No newline at end of file