Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve/comments #42

Merged
merged 3 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@

With a focus on **Clean Architecture** and **extreme code simplicity**, CleanAspire provides developers with the tools to create responsive and maintainable web applications with minimal effort. The template also supports **Microsoft.Kiota** to simplify API client generation, ensuring consistency and productivity in every project.

### 🎉 Auto-generate Code Using Chat GPTs

![chatgpt](./gpts.png)
[Link to CleanAspire Code Generator](https://chatgpt.com/g/g-677e40252ff88191933bb84567b62e7b-cleanaspire-code-generator)


### 🌐 Offline Support

CleanAspire fully supports **offline mode** through its integrated PWA capabilities, enabling your application to function seamlessly without an internet connection. By leveraging **Service Workers** and **browser caching**, the application can store essential resources and data locally, ensuring quick load times and uninterrupted access. Additionally, CleanAspire offers streamlined configuration options to help developers manage caching strategies and data synchronization effortlessly, guaranteeing that users receive the latest updates once the network is restored.
Expand Down Expand Up @@ -66,10 +72,7 @@ By incorporating robust offline capabilities, CleanAspire empowers developers to
- Uses **IndexedDB** to cache data locally, allowing the application to retrieve data and function offline.
- The system detects the online/offline status and fetches data from **IndexedDB** when offline, ensuring uninterrupted access to key features.

### How to generate code use Chat GPTs
![chatgpt](./gpts.png)
[Link to CleanAspire Code Generator](https://chatgpt.com/g/g-677e40252ff88191933bb84567b62e7b-cleanaspire-code-generator)


### How to Create a New Object in a CRUD Application: A Step-by-Step Guide

https://github.com/neozhu/cleanaspire/issues/34
Expand All @@ -95,7 +98,7 @@ https://github.com/neozhu/cleanaspire/issues/34
version: '3.8'
services:
apiservice:
image: blazordevlab/cleanaspire-api:0.0.64
image: blazordevlab/cleanaspire-api:0.0.66
environment:
- ASPNETCORE_ENVIRONMENT=Development
- AllowedHosts=*
Expand All @@ -117,7 +120,7 @@ services:
- "8018:443"

blazorweb:
image: blazordevlab/cleanaspire-webapp:0.0.64
image: blazordevlab/cleanaspire-webapp:0.0.66
environment:
- ASPNETCORE_ENVIRONMENT=Production
- AllowedHosts=*
Expand All @@ -129,7 +132,7 @@ services:
- "8014:443"

standalone:
image: blazordevlab/cleanaspire-standalone:0.0.64
image: blazordevlab/cleanaspire-standalone:0.0.66
ports:
- "8020:80"
- "8021:443"
Expand Down
6 changes: 3 additions & 3 deletions src/CleanAspire.Api/CleanAspire.Api.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
Expand All @@ -18,8 +18,8 @@
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" />
<PackageReference Include="Scalar.AspNetCore" Version="1.2.72" />
<PackageReference Include="Scrutor" Version="5.1.0" />
<PackageReference Include="Scalar.AspNetCore" Version="1.2.74" />
<PackageReference Include="Scrutor" Version="5.1.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
<PackageReference Include="StrongGrid" Version="0.110.0" />
</ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/CleanAspire.Application/CleanAspire.Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@

<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.0.1" />
<PackageReference Include="FluentValidation" Version="11.11.0" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
<PackageReference Include="FluentValidation" Version="12.0.0-preview1" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0-preview1" />
<PackageReference Include="Mediator.Abstractions" Version="3.0.0-preview.27" />
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.0-preview.27">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.0.0-preview-3" />
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.0.0-preview-4" />
<ProjectReference Include="..\CleanAspire.Domain\CleanAspire.Domain.csproj" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,68 +1,58 @@
// This code defines a command and its handler for creating a new product in the database.
// The CreateProductCommand encapsulates the required data to create a product.
// The CreateProductCommandHandler processes the command, creates a product entity, adds a domain event, saves it to the database, and returns a ProductDto.
// Summary:
// This file defines a command and its handler for creating a new product in the database.
// The CreateProductCommand encapsulates the necessary data for a product, while the
// CreateProductCommandHandler processes the command, creates a product entity,
// triggers domain events such as ProductCreatedEvent, and commits the changes. This ensures
// a structured and efficient approach to handling product creation.

using CleanAspire.Application.Features.Products.DTOs;
using CleanAspire.Application.Features.Products.EventHandlers;
using CleanAspire.Application.Pipeline;

// Using directives for necessary namespaces, bringing in external types used in the code
using CleanAspire.Application.Features.Products.DTOs; // Contains data transfer objects (DTOs) for the Product feature
using CleanAspire.Application.Features.Products.EventHandlers; // Contains event handlers related to Product events
using CleanAspire.Application.Pipeline; // Contains pipeline behaviors and related interfaces

// Namespace for organizing related classes and features
namespace CleanAspire.Application.Features.Products.Commands;

// A record that defines the CreateProductCommand, which encapsulates the data needed to create a new product
// Command object that encapsulates the data required for creating a new product.
// Its fields directly map to the properties of ProductDto.
public record CreateProductCommand(
string SKU, // The stock-keeping unit, a unique identifier for the product, corresponding to the SKU field in ProductDto
string Name, // The name of the product, corresponding to the SKU field in ProductDto
ProductCategoryDto? Category, // The category of the product, nullable, referencing ProductCategoryDto definition
string? Description, // A description of the product, nullable, corresponding to the Description field in ProductDto
decimal Price, // The price of the product, matching the Price field in ProductDto
string? Currency, // The currency of the price, nullable, referencing the Currency field in ProductDto
string? UOM // The unit of measure for the product, nullable, consistent with the UOM field in ProductDto
) : IFusionCacheRefreshRequest<ProductDto>, // Implements interface for cache refresh requests
IRequiresValidation // Implements interface for validation requirements
string SKU,
string Name,
ProductCategoryDto? Category,
string? Description,
decimal Price,
string? Currency,
string? UOM
) : IFusionCacheRefreshRequest<ProductDto>,
IRequiresValidation
{
// Optional tags for categorizing the command, useful for logging or debugging
public IEnumerable<string>? Tags => new[] { "products" };
}

// Handler class responsible for processing the CreateProductCommand and returning the result
public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, ProductDto>
{
private readonly IApplicationDbContext _context; // Database context for interacting with the data layer
private readonly IApplicationDbContext _context;

// Constructor to inject dependencies
public CreateProductCommandHandler(IApplicationDbContext context)
{
_context = context;
}

// Asynchronously handles the CreateProductCommand
public async ValueTask<ProductDto> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
// Creates a new Product entity using the data from the command
var product = new Product
{
SKU = request.SKU, // Assigns SKU from command
Name = request.Name, // Assigns Name from command
Category = (ProductCategory)request.Category, // Maps Category DTO to domain entity
Description = request.Description, // Assigns Description from command
Price = request.Price, // Assigns Price from command
Currency = request.Currency, // Assigns Currency from command
UOM = request.UOM // Assigns Unit of Measure from command
SKU = request.SKU,
Name = request.Name,
Category = (ProductCategory)request.Category,

Check warning on line 45 in src/CleanAspire.Application/Features/Products/Commands/CreateProductCommand.cs

View workflow job for this annotation

GitHub Actions / build

Nullable value type may be null.
Description = request.Description,
Price = request.Price,
Currency = request.Currency,
UOM = request.UOM
};

// Adds a domain event to signal that a new product has been created
product.AddDomainEvent(new ProductCreatedEvent(product));

// Adds the new product to the database context
_context.Products.Add(product);

// Saves changes asynchronously to the database
await _context.SaveChangesAsync(cancellationToken);

// Returns a ProductDto containing essential information about the created product
return new ProductDto() { Id = product.Id, Name = product.Name, SKU = product.SKU };
}
}
Original file line number Diff line number Diff line change
@@ -1,51 +1,45 @@
// This code defines a command and its handler for deleting products from a database.
// The DeleteProductCommand encapsulates product IDs, supporting cache refresh and validation.
// The DeleteProductCommandHandler processes the command, removes products, triggers domain events, and saves changes.
// Summary:
// This file defines a command and its handler for deleting products from the database.
// The DeleteProductCommand encapsulates the product IDs to be deleted, while the
// DeleteProductCommandHandler processes the command, removes the corresponding products,
// triggers domain events such as ProductDeletedEvent, and commits the changes. This ensures
// a structured and efficient approach to handling product deletions.

using CleanAspire.Application.Features.Products.EventHandlers;
using CleanAspire.Application.Pipeline;

// A record that defines the DeleteProductCommand, which encapsulates the data needed to delete products by their IDs
using CleanAspire.Application.Features.Products.EventHandlers; // Contains event handlers related to Product events
using CleanAspire.Application.Pipeline; // Contains pipeline behaviors and related interfaces

// Namespace for organizing related classes and features
namespace CleanAspire.Application.Features.Products.Commands;

public record DeleteProductCommand(params IEnumerable<string> Ids) // Takes a list of product IDs as parameters
: IFusionCacheRefreshRequest<Unit>, // Implements interface for cache refresh requests
IRequiresValidation // Implements interface for validation requirements
// Command object that encapsulates the IDs of products to be deleted.
public record DeleteProductCommand(params IEnumerable<string> Ids)
: IFusionCacheRefreshRequest<Unit>,
IRequiresValidation
{
// Optional tags for categorizing the command, useful for logging or debugging
public IEnumerable<string>? Tags => new[] { "products" };
}

// Handler class responsible for processing the DeleteProductCommand
public class DeleteProductCommandHandler : IRequestHandler<DeleteProductCommand>
{
private readonly IApplicationDbContext _dbContext; // Database context for interacting with the data layer
private readonly IApplicationDbContext _dbContext;

// Constructor to inject dependencies, including logger and database context
public DeleteProductCommandHandler(ILogger<DeleteProductCommandHandler> logger, IApplicationDbContext dbContext)
{
_dbContext = dbContext;
}

// Asynchronously handles the DeleteProductCommand
public async ValueTask<Unit> Handle(DeleteProductCommand request, CancellationToken cancellationToken)
{
// Retrieves products from the database that match the provided IDs
var products = _dbContext.Products.Where(p => request.Ids.Contains(p.Id));

// Iterates through each product to add a deletion domain event and remove it from the database context
foreach (var product in products)
{
product.AddDomainEvent(new ProductDeletedEvent(product)); // Adds domain event for product deletion
_dbContext.Products.Remove(product); // Removes product from the database
product.AddDomainEvent(new ProductDeletedEvent(product));
_dbContext.Products.Remove(product);
}

// Saves changes asynchronously to the database
await _dbContext.SaveChangesAsync(cancellationToken);

// Returns a Unit value to signal successful completion
return Unit.Value;
}
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// This code defines a command and its handler for importing products from a CSV file.
// The ImportProductsCommand encapsulates the input stream, supporting cache refresh and validation.
// The ImportProductsCommandHandler processes the command, reads CSV data, maps to entities, adds domain events, and saves changes.

// Summary:
// This file defines a command and its handler for importing products from a CSV file.
// The ImportProductsCommand encapsulates the input stream, while the ImportProductsCommandHandler
// reads the CSV data, maps it to product entities, triggers domain events like ProductCreatedEvent,
// and commits the changes to the database.

using System.Globalization;
using CleanAspire.Application.Features.Products.DTOs;
Expand All @@ -10,66 +11,51 @@
using CsvHelper;

namespace CleanAspire.Application.Features.Products.Commands;
// A record that defines the ImportProductsCommand, which encapsulates the data needed to import products from a CSV file
public record ImportProductsCommand(Stream Stream // Stream containing CSV data
) : IFusionCacheRefreshRequest<Unit>, // Implements interface for cache refresh requests
IRequiresValidation // Implements interface for validation requirements

// Command object that encapsulates the input stream containing CSV data.
public record ImportProductsCommand(Stream Stream)
: IFusionCacheRefreshRequest<Unit>,
IRequiresValidation
{
// Optional tags for categorizing the command, useful for logging or debugging
public IEnumerable<string>? Tags => new[] { "products" };
}

// Handler class responsible for processing the ImportProductsCommand
public class ImportProductsCommandHandler : IRequestHandler<ImportProductsCommand, Unit>
{
private readonly IApplicationDbContext _context; // Database context for interacting with the data layer
private readonly IApplicationDbContext _context;

// Constructor to inject dependencies
public ImportProductsCommandHandler(IApplicationDbContext context)
{
_context = context;
}

// Asynchronously handles the ImportProductsCommand
public async ValueTask<Unit> Handle(ImportProductsCommand request, CancellationToken cancellationToken)
{
// Resets the stream position to the beginning to ensure correct reading
request.Stream.Position = 0;

// Uses CsvHelper to read and parse the CSV data
using (var reader = new StreamReader(request.Stream))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
// Maps CSV records to ProductDto objects
var records = csv.GetRecords<ProductDto>();

// CsvHelper automatically handles parsing and mapping of CSV fields to object properties
// based on their headers and property names, ensuring accuracy and reducing boilerplate code.

// Iterates through each ProductDto and maps it to a Product entity
foreach (var product in records.Select(x => new Product()
foreach (var product in records.Select(x => new Product
{
SKU = x.SKU, // Maps SKU from ProductDto
Name = x.Name, // Maps Name from ProductDto
Category = (ProductCategory)x.Category, // Maps Category from ProductDto
Description = x.Description, // Maps Description from ProductDto
Price = x.Price, // Maps Price from ProductDto
Currency = x.Currency, // Maps Currency from ProductDto
UOM = x.UOM // Maps Unit of Measure from ProductDto
SKU = x.SKU,
Name = x.Name,
Category = (ProductCategory)x.Category,

Check warning on line 45 in src/CleanAspire.Application/Features/Products/Commands/ImportProductsCommand.cs

View workflow job for this annotation

GitHub Actions / build

Nullable value type may be null.
Description = x.Description,
Price = x.Price,
Currency = x.Currency,
UOM = x.UOM
}))
{
// Adds a domain event for product creation
product.AddDomainEvent(new ProductCreatedEvent(product));

// Adds the new product to the database context
_context.Products.Add(product);
}

// Saves changes asynchronously to the database
await _context.SaveChangesAsync(cancellationToken);
}

// Returns a Unit value to signal successful completion
return Unit.Value;
}
}
Loading
Loading