Update clean-architecture-dotnet.html
ยท 1 year ago
bbbdf41ad4a8b09b55e922c9c841197d74f01371
Parent:
a3a50e32e
1 file changed +195 โ41
- clean-architecture-dotnet.html +195 โ41
Diff
--- a/clean-architecture-dotnet.html +++ b/clean-architecture-dotnet.html @@ -1,21 +1,20 @@ <!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, explaining its need, core principles, layers, C# examples, project structure, and best practices.." name="description"/> +<meta content="A comprehensive cheatsheet for Clean Architecture in .NET Web API projects, explaining its need, core principles, layers, C# examples, project structure, Visual Studio setup, 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, Introduction to Clean Architecture, Best Practices" name="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, Introduction to Clean Architecture, Best Practices, Visual Studio Setup" name="keywords"/> <!-- Canonical URL --> <link href="https://cheatsheets.davidveksler.com/clean-architecture-dotnet.html" rel="canonical"/> <!-- Favicon --> <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 & Twitter) --> <meta content="Clean Architecture in .NET Web API: Comprehensive Cheatsheet" property="og:title"/> -<meta content="Why use Clean Architecture? Explore its benefits, principles, layers, and implementation in .NET Web API. Includes C# examples and best practices.." property="og:description"/> +<meta content="Why use Clean Architecture? Explore its benefits, principles, layers, implementation in .NET Web API, Visual Studio setup. Includes C# examples and best practices.." property="og:description"/> <meta content="website" property="og:type"/> <meta content="https://cheatsheets.davidveksler.com/clean-architecture-dotnet.html" property="og:url"/> <meta content="summary_large_image" name="twitter:card"/> @@ -25,13 +24,13 @@ "@context": "https://schema.org", "@type": "TechArticle", "headline": "Clean Architecture in .NET Web API: Comprehensive Cheatsheet Version ", - "description": "A comprehensive guide to Clean Architecture in .NET Web API, starting with why it's needed, then detailing layers, core principles, project structure, C# examples, and best practices.", + "description": "A comprehensive guide to Clean Architecture in .NET Web API, starting with why it's needed, then detailing layers, core principles, project structure, Visual Studio setup, C# examples, and best practices.", "image": "https://cheatsheets.davidveksler.com/images/clean-architecture-dotnet.png", "author": { "@type": "Person", "name": "David Veksler (AI Generated)" }, "publisher": { "@type": "Organization", "name": "David Veksler Cheatsheets" }, "datePublished": "2025-06-10", "dateModified": "2025-06-10", - "keywords": "Clean Architecture Introduction, Why Clean Architecture, .NET, Web API, ASP.NET Core, C#, Software Architecture, DDD, Best Practices" + "keywords": "Clean Architecture Introduction, Why Clean Architecture, .NET, Web API, ASP.NET Core, C#, Software Architecture, DDD, Best Practices, Visual Studio Setup" } </script> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/> @@ -117,6 +116,10 @@ .collapse-content pre { /* ... */ } .collapse-content pre code { /* ... */ } .collapse-content pre::before { /* ... */ } + .collapse-content ol { padding-left: 1.5rem; } + .collapse-content ol li { margin-bottom: 0.75rem; } + .collapse-content ol ul { list-style-type: disc; margin-top: 0.5rem; } + .term { /* ... */ } .term:hover { /* ... */ } @@ -127,7 +130,7 @@ footer a:hover { /* ... */ } @media print { /* ... (styles from previous version, no changes here) ... */ } - </style> +</style> <style> :root { --bs-body-bg: #f8f9fa; @@ -355,6 +358,11 @@ font-family: 'Inter', sans-serif; font-weight: 500; } + .collapse-content ol { padding-left: 1.5rem; margin-bottom: 1rem; list-style-type: decimal; } + .collapse-content ol > li { margin-bottom: 0.75rem; font-size: 0.92rem; padding-left: 0.3rem; } + .collapse-content ol ul { list-style-type: disc; margin-top: 0.5rem; padding-left: 1.2rem; } + .collapse-content ol ul li { margin-bottom: 0.4rem; font-size: 0.9rem; } + .term { font-weight: 600; color: var(--bs-primary); @@ -396,6 +404,8 @@ .collapse-content { border-top: 1px solid #eee; background-color: #fff; } .collapse-content pre, .collapse-content pre code, .info-card .card-body pre, .info-card .card-body pre code { background-color: #f8f9fa !important; color: #212529 !important; border: 1px solid #eee; white-space: pre-wrap; word-break: break-all; } .collapse-content pre::before { display: none; } + .collapse-content ol, .collapse-content ol ul { list-style-type: decimal !important; } + .collapse-content ol ul { list-style-type: disc !important; } .term { background-color: #e9ecef; border: 1px solid #ced4da; color: #000; padding: 0.1em 0.2em; } a { text-decoration: none; color: #000; } a[href^="http"]:after { content: " (" attr(href) ")"; font-size: 0.9em; } @@ -435,6 +445,10 @@ <li><strong>Independent of Database:</strong> You can swap Oracle or SQL Server, for Mongo or BigTable. Your business rules are not bound to the database.</li> <li><strong>Independent of External Agencies:</strong> Your business rules don't know anything about the outside world.</li> <li><strong>Improved Maintainability & Flexibility:</strong> Changes to external concerns have minimal impact on core logic, making the system easier to evolve and adapt.</li> +<li><strong>Promotes a development focus on core business logic</strong>, leading to more accurate implementation.</li> +<li><strong>Helps maintain consistent coding practices</strong>, improving application stability and security.</li> +<li><strong>Aids in quickly adding new features</strong>, APIs, and third-party components.</li> +<li><strong>Implements abstraction effectively.</strong></li> </ul> <h6>When is Clean Architecture Most Beneficial?</h6> <ul> @@ -442,6 +456,8 @@ <li>In long-lived projects expected to undergo evolution, maintenance, and potential technology shifts over many years.</li> <li>When high degrees of testability and maintainability are paramount project goals.</li> <li>If there's a strategic need for independence from specific external frameworks or technologies, ensuring future-proofing and adaptability.</li> +<li>When the software needs to closely follow <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></strong> principles.</li> +<li>When there is a need for the architecture to <strong>help enforce specific development policies and standards.</strong></li> </ul> <p class="mt-3"><strong>Important Note:</strong> Clean Architecture is not a silver bullet. It introduces a degree of initial setup complexity and requires discipline. For very simple CRUD applications or short-lived projects, the overhead might not be justified. However, for complex, evolving systems, the long-term benefits in resilience, adaptability, and reduced maintenance costs often far outweigh the upfront investment.</p> </div> @@ -463,6 +479,9 @@ <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 via Interfaces</span>:</strong> Inner layers define abstractions (interfaces); outer layers provide concrete implementations. This inverts traditional control flow for dependencies.</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.">High Testability</span>:</strong> Business logic (Domain & Application layers) can be unit-tested independently of UI, database, or any external service.</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.">Enhanced Maintainability & Flexibility</span>:</strong> The decoupling makes the system easier to understand, modify, and evolve. Changes in one area (e.g., database technology) are less likely to ripple through the entire codebase.</li> +<li><strong>Focus on Core Logic:</strong> Business rules and entities are modeled within the Core (Domain) project, independent of other concerns.</li> +<li><strong>Unidirectional Dependencies:</strong> All source code dependencies must flow inwards, towards the Core project. Outer layers depend on the Core; the Core does not depend on any outer layer.</li> +<li><strong>Interface Definition:</strong> Inner layers (Domain, Application) define interfaces, and outer layers (Infrastructure, Presentation) implement them.</li> </ul> </div> </div> @@ -483,7 +502,7 @@ </div> </div> </div> -<!-- Layers Overview & Responsibilities (Content is the same as previous, only wrapper styles changed) --> +<!-- 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"> @@ -521,8 +540,10 @@ public class Product <li><strong><span class="term" data-bs-toggle="tooltip" title="Immutable objects representing descriptive aspects of the domain, identified by their attributes rather than an ID (e.g., Address, Money).">Value Objects</span>:</strong> Immutable objects defined by their attributes, not an ID.</li> <li><strong><span class="term" data-bs-toggle="tooltip" title="A cluster of domain objects (entities and value objects) that are treated as a single unit for data changes. An Aggregate Root is the main entity that controls access to the aggregate and ensures consistency.">Aggregates</span>:</strong> Clusters of domain objects ensuring consistency within a boundary, with an Aggregate Root entity.</li> <li><strong><span class="term" data-bs-toggle="tooltip" title="Operations that don't naturally belong to a single entity, often coordinating logic between multiple entities or aggregates.">Domain Services</span>:</strong> Business logic that doesn't naturally fit within a single entity.</li> -<li><strong><span class="term" data-bs-toggle="tooltip" title="Abstractions for data persistence, defined in the Domain layer but implemented in the Infrastructure layer (e.g., IProductRepository). These define the contract for how domain objects are retrieved and stored.">Repository Interfaces</span>:</strong> Abstractions for data persistence operations. - <pre><code class="language-csharp">// Domain/Interfaces/IProductRepository.cs +<li><strong>Interfaces:</strong> Abstractions defined by the Domain layer. + <ul> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Abstractions for data persistence, defined in the Domain layer but implemented in the Infrastructure layer (e.g., IProductRepository). These define the contract for how domain objects are retrieved and stored.">Repository Interfaces</span>:</strong> Contracts for data persistence operations (e.g., <code>IProductRepository</code>). + <pre><code class="language-csharp">// Domain/Interfaces/IProductRepository.cs public interface IProductRepository { Task<Product?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default); @@ -531,9 +552,16 @@ public interface IProductRepository Task UpdateAsync(Product product, CancellationToken cancellationToken = default); Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); }</code></pre> + </li> + <li>Other domain-specific service interfaces.</li> + </ul> </li> <li><strong>Domain Events:</strong> Represent something significant that happened in the domain. Used for side effects and decoupling.</li> <li><strong>Custom Domain Exceptions:</strong> Specific exceptions to signal violations of domain rules (e.g., `InsufficientStockException`).</li> +<li><strong><span class="term" data-bs-toggle="tooltip" title="Objects that encapsulate query logic, often used with repositories to build complex queries in a reusable and type-safe manner.">Specifications</span>:</strong> (e.g., for defining complex query criteria in a reusable way).</li> +<li><strong>Domain-Specific <span class="term" data-bs-toggle="tooltip" title="Classes or methods responsible for validating domain objects against specific business rules or ensuring pre/post conditions (invariants) are met.">Validators/Guards</span>:</strong> For validating entities against business rules and ensuring invariants.</li> +<li><strong>Enums:</strong> Domain-specific enumerations.</li> +<li><strong>(Event Handlers - Domain-Level, if applicable):</strong> Handlers for domain events orchestrating logic *within* the domain.</li> </ul> </div> </div> @@ -551,7 +579,9 @@ public interface IProductRepository <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 or workflows, often corresponding to user actions or system events. Commonly implemented using the CQRS pattern.">Use Cases/Interactors/Services</span>:</strong> (often as CQRS Commands and Queries) +<li><strong>Use Cases/Interactors:</strong> + <ul> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Objects that encapsulate an operation that modifies the system's state. They are processed by Command Handlers.">Commands</span> & <span class="term" data-bs-toggle="tooltip" title="Classes responsible for processing a specific Command and executing the corresponding business logic.">Command Handlers</span>:</strong> Encapsulate operations that change the system's state. <pre><code class="language-csharp">// Application/Products/Commands/CreateProductCommand.cs public record CreateProductCommand(string Name, decimal Price) : IRequest<Guid>; @@ -574,12 +604,16 @@ public class CreateProductCommandHandler : IRequestHandler<CreateProductComma return product.Id; } }</code></pre> + </li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Objects that encapsulate a request for data. They do not modify the system's state and are processed by Query Handlers.">Queries</span> & <span class="term" data-bs-toggle="tooltip" title="Classes responsible for processing a specific Query and retrieving data from the persistence layer, often mapping it to DTOs.">Query Handlers</span>:</strong> Encapsulate operations that retrieve data without altering state.</li> + </ul> </li> <li><strong><span class="term" data-bs-toggle="tooltip" title="Simple, typically immutable, objects used to transfer data between layers, particularly between Application and Presentation. They help decouple layers and prevent leaking domain models.">Data Transfer Objects (DTOs)</span>:</strong> For transferring data between layers.</li> <li><strong>Interfaces for Infrastructure Services:</strong> Abstractions for infrastructure concerns (e.g., `IEmailSender`, `IDateTimeProvider`, `IFileStorage`).</li> -<li><strong>Validation Logic:</strong> For input data (e.g., using <span class="term" data-bs-toggle="tooltip" title="A popular .NET library for creating strongly-typed validation rules, often used with commands and DTOs.">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. Often implemented with MediatR.">CQRS</span>:</strong> Separates read and write operations. <span class="term" data-bs-toggle="tooltip" title="A popular in-process messaging library in .NET that helps implement Mediator and CQRS patterns by decoupling message senders from their handlers.">MediatR</span> is a common tool for this.</li> +<li><strong>Validation Logic:</strong> For input data (e.g., using <span class="term" data-bs-toggle="tooltip" title="A popular .NET library for creating strongly-typed validation rules, often used with commands and DTOs.">FluentValidation</span>, often as MediatR pipeline behaviors).</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. Often implemented with MediatR.">CQRS</span> Pattern Support:</strong> Tools like <span class="term" data-bs-toggle="tooltip" title="A popular in-process messaging library in .NET that helps implement Mediator and CQRS patterns by decoupling message senders from their handlers.">MediatR</span> are commonly used here to separate commands and queries.</li> <li><strong>Application Exceptions:</strong> For errors specific to application logic (e.g., `ValidationException`, `NotFoundException`).</li> +<li><strong><span class="term" data-bs-toggle="tooltip" title="In MediatR, these are classes that can intercept requests (commands/queries) and responses in the pipeline, allowing for cross-cutting concerns like logging, validation, caching, etc.">Pipeline Behaviors</span> (e.g., for MediatR):</strong> For cross-cutting concerns like validation, logging, or caching applied to use cases.</li> </ul> </div> </div> @@ -597,7 +631,9 @@ public class CreateProductCommandHandler : IRequestHandler<CreateProductComma <div class="collapse collapse-content" id="detailsInfrastructureLayer"> <h6>Key Components:</h6> <ul> -<li><strong>Data Access/Persistence:</strong> Implementations of repository interfaces (e.g., using Entity Framework Core). Includes `DbContext`. +<li><strong>Data Access/Persistence Implementations:</strong> + <ul> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Concrete classes that implement the repository interfaces defined in the Domain layer, typically using an ORM like Entity Framework Core.">Repositories</span>:</strong> Concrete implementations of repository interfaces (e.g., using Entity Framework Core). <pre><code class="language-csharp">// Infrastructure/Persistence/Repositories/ProductRepository.cs public class ProductRepository : IProductRepository { @@ -607,9 +643,10 @@ public class ProductRepository : IProductRepository public async Task AddAsync(Product product, CancellationToken ct) => await _dbContext.Products.AddAsync(product, ct); // ... etc. -} - -// Infrastructure/Persistence/ApplicationDbContext.cs (EF Core example) +}</code></pre> + </li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="In Entity Framework Core, this class represents a session with the database and allows querying and saving data. It's a key part of the Unit of Work pattern.">DbContext</span>:</strong> EF Core `DbContext` class for database interaction. + <pre><code class="language-csharp">// Infrastructure/Persistence/ApplicationDbContext.cs (EF Core example) public class ApplicationDbContext : DbContext, IUnitOfWork // Explicitly implementing IUnitOfWork { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } @@ -627,11 +664,23 @@ public class ApplicationDbContext : DbContext, IUnitOfWork // Explicitly impleme return await base.SaveChangesAsync(cancellationToken); } }</code></pre> + </li> + <li>Database migrations.</li> + </ul> +</li> +<li><strong>External Service Integrations:</strong> + <ul> + <li>Clients for payment gateways, **<span class="term" data-bs-toggle="tooltip" title="Implementations of email sending services, e.g., using SMTP or third-party email APIs like SendGrid.">email services (Emailing Implementations)</span>**, **<span class="term" data-bs-toggle="tooltip" title="Implementations for sending SMS messages, e.g., via Twilio or other providers.">SMS services</span>**, third-party APIs.</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="Components that interact with cloud storage services like AWS S3, Azure Blob Storage, or Google Cloud Storage for file management.">Cloud service accessors</span>** (e.g., Azure Storage, AWS S3).</li> + <li><strong><span class="term" data-bs-toggle="tooltip" title="HTTP clients or SDK wrappers for consuming external APIs or microservices.">API Clients</span>** for consuming other services.</li> + </ul> </li> -<li><strong>External Service Integrations:</strong> Clients for payment gateways, email services, third-party APIs.</li> <li><strong>Caching Implementations:</strong> (e.g., Redis, In-Memory).</li> -<li><strong>Identity Services:</strong> User authentication and authorization implementations.</li> -<li><strong>File System Access, Clock Services, Logging Implementations, etc.</strong></li> +<li><strong>Identity Service Implementations:</strong> User authentication and authorization.</li> +<li><strong><span class="term" data-bs-toggle="tooltip" title="Classes that provide access to the file system for reading or writing files.">File System Accessors/Implementations</span>.</strong></li> +<li><strong><span class="term" data-bs-toggle="tooltip" title="Concrete implementations of date/time provider interfaces, allowing for testable date/time logic.">Clock/DateTime Service Implementations</span>.</strong></li> +<li><strong><span class="term" data-bs-toggle="tooltip" title="Concrete implementations of logging interfaces, integrating with logging frameworks like Serilog or NLog.">Logging Service Implementations</span>.</strong></li> +<li><strong>Other Infrastructure Services:</strong> (e.g., concrete implementations of any other interfaces defined by the Application layer for infrastructure concerns).</li> </ul> </div> </div> @@ -677,10 +726,13 @@ public class ProductsController : ControllerBase } }</code></pre> </li> -<li><strong>Middleware:</strong> For cross-cutting concerns like global error handling, authentication, request 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. This is where layers are composed.">Dependency Injection (DI) Setup</span>:</strong> Configuration of services and their dependencies (typically in `Program.cs`). This is the "Composition Root".</li> +<li><strong><span class="term" data-bs-toggle="tooltip" title="Software components that sit in the request processing pipeline, handling cross-cutting concerns like authentication, logging, or global error handling.">Middleware</span>:</strong> For cross-cutting concerns like global error handling, authentication, request 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. This is where layers are composed.">Dependency Injection (DI) Setup / Composition Root</span>:</strong> Configuration of services and their dependencies (typically in `Program.cs`). This is the "Composition Root".</li> <li><strong>API Models/ViewModels:</strong> Models specific to API requests/responses. Often mapped to/from Application layer DTOs.</li> <li><strong>API Versioning, OpenAPI/Swagger Configuration.</strong></li> +<li><strong><span class="term" data-bs-toggle="tooltip" title="Attributes or classes that can execute code before or after controller actions or during other stages of request processing, used for concerns like authorization, caching, or custom error handling.">Filters (Action Filters, Exception Filters, etc.)</span>:</strong> For cross-cutting concerns specific to API request processing.</li> +<li><strong><span class="term" data-bs-toggle="tooltip" title="Custom components that can transform incoming request data (e.g., from query strings, route data, or request body) into complex types expected by controller actions.">Model Binders</span>:</strong> Custom logic for binding request data to models.</li> +<li>(Other UI-specific services if not strictly a Web API, e.g., for MVC: ViewModels, Tag Helpers - though less relevant for a pure Web API cheatsheet).</li> </ul> </div> </div> @@ -688,38 +740,40 @@ public class ProductsController : ControllerBase </div> </div> </div> -<!-- Project Structure Example --> +<!-- Project Structure Example & Visual Studio Setup --> <div class="schema-container section-project-structure"> -<h2 class="section-title"><i class="bi bi-diagram-3-fill"></i> Project Structure Example</h2> +<h2 class="section-title"><i class="bi bi-diagram-3-fill"></i> Project Structure & Visual Studio Setup</h2> <div class="info-card card-project-structure"> -<div class="card-header"><i class="bi bi-folder2-open"></i> Solution Structure</div> +<div class="card-header"><i class="bi bi-folder2-open"></i> Typical Solution Structure</div> <div class="card-body"> <p>A common way to organize projects in a .NET solution reflecting Clean Architecture:</p> <pre><code class="language-text"> -Solution.sln +YourProjectSolution.sln โโโ src -โ โโโ Domain/ (Core.csproj - .NET Class Library) +โ โโโ YourProject.Domain.csproj (.NET Class Library) โ โ โโโ Entities/ โ โ โโโ Aggregates/ โ โ โโโ Enums/ โ โ โโโ Events/ โ โ โโโ Exceptions/ โ โ โโโ Interfaces/ (e.g., IProductRepository.cs) +โ โ โโโ Specifications/ +โ โ โโโ Validators/Guards/ โ โ โโโ ValueObjects/ โ โ -โ โโโ Application/ (Application.csproj - .NET Class Library) +โ โโโ YourProject.Application.csproj (.NET Class Library) โ โ โโโ Features/ (Organized by feature, e.g., Products, Orders) โ โ โ โโโ Products/ โ โ โ โ โโโ Commands/ โ โ โ โ โโโ Queries/ โ โ โ โ โโโ DTOs/ โ โ โโโ Common/ -โ โ โ โโโ Interfaces/ (e.g., IEmailSender.cs) -โ โ โ โโโ Behaviors/ (MediatR pipeline behaviors for validation, logging) +โ โ โ โโโ Interfaces/ (e.g., IEmailSender.cs, IDateTimeProvider.cs) +โ โ โ โโโ Behaviors/ (MediatR pipeline behaviors) โ โ โ โโโ Mappings/ (AutoMapper profiles if used) โ โ โโโ Exceptions/ โ โ -โ โโโ Infrastructure/ (Infrastructure.csproj - .NET Class Library) +โ โโโ YourProject.Infrastructure.csproj (.NET Class Library) โ โ โโโ Persistence/ โ โ โ โโโ DataContext/ (e.g., ApplicationDbContext.cs) โ โ โ โโโ Repositories/ (e.g., ProductRepository.cs) @@ -728,19 +782,89 @@ Solution.sln โ โ โโโ Services/ (e.g., EmailSender.cs, DateTimeProvider.cs) โ โ โโโ Identity/ โ โ -โ โโโ Presentation/ (WebApi.csproj - ASP.NET Core Web API Project) +โ โโโ YourProject.WebApi.csproj (ASP.NET Core Web API Project) โ โโโ Controllers/ โ โโโ Middleware/ +โ โโโ Filters/ โ โโโ Extensions/ (Service registration extensions) โ โโโ appsettings.json -โ โโโ Program.cs +โ โโโ Program.cs (Composition Root) โ โโโ tests - โโโ Domain.UnitTests/ - โโโ Application.UnitTests/ - โโโ Infrastructure.IntegrationTests/ (May require test databases/services) - โโโ Presentation.IntegrationTests/ (API endpoint tests) + โโโ YourProject.Domain.UnitTests.csproj + โโโ YourProject.Application.UnitTests.csproj + โโโ YourProject.Infrastructure.IntegrationTests.csproj + โโโ YourProject.Presentation.IntegrationTests.csproj </code></pre> +<button aria-controls="detailsVsSetup" aria-expanded="false" class="btn btn-sm details-toggle" data-bs-target="#detailsVsSetup" data-bs-toggle="collapse" type="button"> + Visual Studio Setup Guide <i class="bi bi-chevron-down"></i> +</button> +<div class="collapse collapse-content" id="detailsVsSetup"> +<h6>Setting up the Projects in Visual Studio:</h6> +<ol> + <li><strong>Create the Solution and Presentation Layer (Web API):</strong> + <ul> + <li>Open Visual Studio.</li> + <li>Select "Create a new project".</li> + <li>Choose the "ASP.NET Core Web API" template. Click Next.</li> + <li>Name your project (e.g., `YourProject.WebApi`) and solution (e.g., `YourProjectSolution`). Click Next.</li> + <li>Choose your desired .NET framework version. Configure other settings as needed. Click Create.</li> + <li>This project will serve as your <strong>Presentation Layer</strong>.</li> + </ul> + </li> + <li><strong>Add the Domain Layer:</strong> + <ul> + <li>In Solution Explorer, right-click on the Solution (`YourProjectSolution`).</li> + <li>Select Add > New Project...</li> + <li>Choose the "Class Library" template. Click Next.</li> + <li>Name the project `YourProject.Domain`. Click Next.</li> + <li>Choose the same .NET framework version as your Web API project. Click Create.</li> + <li>Delete the default `Class1.cs` file.</li> + <li>Create folders like `Entities`, `Interfaces`, `ValueObjects`, etc., as shown in the structure above.</li> + <li><em>Important:</em> The Domain project should NOT have project references to any other layer project in this solution.</li> + </ul> + </li> + <li><strong>Add the Application Layer:</strong> + <ul> + <li>Right-click on the Solution > Add > New Project...</li> + <li>Choose "Class Library". Name it `YourProject.Application`.</li> + <li>Ensure the .NET framework version matches. Click Create.</li> + <li>Delete `Class1.cs`.</li> + <li>Create folders like `Features`, `Common/Interfaces`, `DTOs`, etc.</li> + <li><strong>Add Project Reference:</strong> Right-click on the `YourProject.Application` project > Add > Project Reference... > Check `YourProject.Domain` > OK.</li> + </ul> + </li> + <li><strong>Add the Infrastructure Layer:</strong> + <ul> + <li>Right-click on the Solution > Add > New Project...</li> + <li>Choose "Class Library". Name it `YourProject.Infrastructure`.</li> + <li>Ensure the .NET framework version matches. Click Create.</li> + <li>Delete `Class1.cs`.</li> + <li>Create folders like `Persistence/DataContext`, `Repositories`, `Services`, etc.</li> + <li><strong>Add Project Reference:</strong> Right-click on the `YourProject.Infrastructure` project > Add > Project Reference... > Check `YourProject.Application` > OK. (The Infrastructure layer implements interfaces defined in Application and Domain).</li> + <li>Install necessary NuGet packages here (e.g., `Microsoft.EntityFrameworkCore`, database providers, SDKs).</li> + </ul> + </li> + <li><strong>Configure Presentation Layer Dependencies:</strong> + <ul> + <li>The `YourProject.WebApi` (Presentation) project orchestrates the application.</li> + <li><strong>Add Project References:</strong> Right-click on the `YourProject.WebApi` project > Add > Project Reference... > Check `YourProject.Application` and `YourProject.Infrastructure` > OK.</li> + <li>This allows the Web API to send commands/queries to the Application layer and to register services (dependency injection) from the Infrastructure layer in `Program.cs`.</li> + </ul> + </li> + <li><strong>Set Startup Project:</strong> + <ul> + <li>Right-click on the `YourProject.WebApi` project in Solution Explorer and select "Set as Startup Project".</li> + </ul> + </li> +</ol> +<p class="mt-3"><strong>Dependency Flow Reminder:</strong><br/> +<code>Presentation (WebApi)</code> โ references <code>Application</code> & <code>Infrastructure</code><br/> +<code>Infrastructure</code> โ references <code>Application</code> (to implement its interfaces and use its DTOs/Domain types)<br/> +<code>Application</code> โ references <code>Domain</code><br/> +<code>Domain</code> โ references (No other project dependencies within the solution) +</p> +</div> </div> </div> </div> @@ -794,9 +918,36 @@ Solution.sln } const lastUpdatedDateSpan = document.getElementById('lastUpdatedDate'); if (lastUpdatedDateSpan) { - const today = new Date(); + const today = new Date(); // Will reflect the current date on load + const jsonLdScript = document.querySelector('script[type="application/ld+json"]'); + let datePublished = "2025-06-10"; // Default fallback from original script + if (jsonLdScript) { + try { + const jsonData = JSON.parse(jsonLdScript.textContent); + datePublished = jsonData.datePublished || jsonData.dateModified || datePublished; + } catch (e) { + console.error("Error parsing JSON-LD: ", e); + } + } + // For display, always show today's date as "Last Updated" const options = { year: 'numeric', month: 'long', day: 'numeric' }; lastUpdatedDateSpan.textContent = today.toLocaleDateString('en-US', options); + + // Update JSON-LD dateModified to today + if (jsonLdScript) { + try { + const jsonData = JSON.parse(jsonLdScript.textContent); + const isoDate = today.toISOString().split('T')[0]; + jsonData.dateModified = isoDate; + // If datePublished is in the future based on original script, set it to today as well + if (new Date(jsonData.datePublished) > today) { + jsonData.datePublished = isoDate; + } + jsonLdScript.textContent = JSON.stringify(jsonData, null, 2); + } catch (e) { + console.error("Error updating JSON-LD: ", e); + } + } } var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); @@ -819,14 +970,17 @@ Solution.sln if (collapseEl.classList.contains('show')) { iconEl.classList.remove('bi-chevron-down'); iconEl.classList.add('bi-chevron-up'); + button.setAttribute('aria-expanded', 'true'); } else { iconEl.classList.remove('bi-chevron-up'); iconEl.classList.add('bi-chevron-down'); + button.setAttribute('aria-expanded', 'false'); } }; - updateIconState(); - collapseEl.addEventListener('show.bs.collapse', updateIconState); - collapseEl.addEventListener('hide.bs.collapse', updateIconState); + // Initial state based on class, then event listeners + updateIconState(); + collapseEl.addEventListener('shown.bs.collapse', updateIconState); + collapseEl.addEventListener('hidden.bs.collapse', updateIconState); } });