From 5535011df9ce9a34b6379c094fd0f8486b322376 Mon Sep 17 00:00:00 2001 From: Loretta Date: Mon, 30 Mar 2026 10:26:35 +0200 Subject: [PATCH] Comprehensive documentation overhaul and expansion - Added detailed .md docs for Core, Data, and Services projects: DTOs, NopDependencies, repositories, transactions, services, and logging bridge - Updated all project README.md files with metadata, documentation tables, inheritance diagrams, and clearer dependency/reference explanations - Root README.md now includes a documentation map and improved project/type tables - Expanded and clarified `.github/copilot-instructions.md` as the single source of truth for domain rules, patterns, and conventions - Updated ARCHITECTURE.md, CONVENTIONS.md, and GLOSSARY.md to document DTO strategies, transaction and logging patterns, project boundaries, and AyCode integration - Added CLAUDE.md for Claude-specific code authoring guidance - All .csproj files now include new docs as non-compilable content - Ensured clear separation of concerns and improved AI/tooling discoverability across the codebase --- .github/copilot-instructions.md | 41 +++++--- CLAUDE.md | 10 ++ Mango.Nop.Core/Mango.Nop.Core.csproj | 5 + Mango.Nop.Core/README.md | 103 ++++++++++++++----- Mango.Nop.Core/docs/DTOS.md | 74 +++++++++++++ Mango.Nop.Core/docs/NOP_DEPENDENCIES.md | 46 +++++++++ Mango.Nop.Data/Mango.Nop.Data.csproj | 6 ++ Mango.Nop.Data/README.md | 49 +++++++-- Mango.Nop.Data/docs/REPOSITORIES.md | 42 ++++++++ Mango.Nop.Data/docs/TRANSACTIONS.md | 53 ++++++++++ Mango.Nop.Services/Mango.Nop.Services.csproj | 6 ++ Mango.Nop.Services/README.md | 28 +++-- Mango.Nop.Services/docs/LOGGING_BRIDGE.md | 40 +++++++ Mango.Nop.Services/docs/SERVICES.md | 71 +++++++++++++ README.md | 36 ++++++- docs/ARCHITECTURE.md | 84 +++++++++++++-- docs/CONVENTIONS.md | 76 ++++++++++++-- docs/GLOSSARY.md | 57 +++++++--- 18 files changed, 738 insertions(+), 89 deletions(-) create mode 100644 CLAUDE.md create mode 100644 Mango.Nop.Core/docs/DTOS.md create mode 100644 Mango.Nop.Core/docs/NOP_DEPENDENCIES.md create mode 100644 Mango.Nop.Data/docs/REPOSITORIES.md create mode 100644 Mango.Nop.Data/docs/TRANSACTIONS.md create mode 100644 Mango.Nop.Services/docs/LOGGING_BRIDGE.md create mode 100644 Mango.Nop.Services/docs/SERVICES.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 696b16e..f89456f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,8 +1,14 @@ # Mango.Nop Libraries — Domain Rules +@repo { + type = "framework" + layer = 1 + own-dep-repos = ["AyCode.Core"] +} + > This is the **single source of truth** for Mango.Nop library rules. Do not duplicate these elsewhere. > For detailed docs see: `README.md` → `docs/` -> For core framework rules see: `../../../../../../Aycode/Source/AyCode.Core/.github/copilot-instructions.md` +> For core framework rules see: `.github/copilot-instructions.md` (in AyCode.Core repo) ## nopCommerce Compatibility 1. All three libraries target **net9.0** — required by nopCommerce 4.80.9. Do NOT change TFM. @@ -10,22 +16,31 @@ 3. Do NOT modify files in `NopDependencies/` unless the nopCommerce version changes. ## Entity & DTO Patterns -4. **`BaseEntity`** (NopDependencies) = root entity base with `int Id`. All nopCommerce entities inherit from this. -5. **`MgEntityBase`** = custom entity base that inherits `BaseEntity` and implements `IEntityInt` (from AyCode.Interfaces). Used for domain-specific entities. -6. **`ModelDtoBase`** = DTO base with two-way mapping: `CopyEntityValuesToDto()` and `CreateMainEntity()`. All DTOs inherit from this. -7. **`GenericAttribute`** = polymorphic key-value store. `KeyGroup` = owner type name, `EntityId` = owner ID. Extension methods in `GenericAttributeExtensions` provide typed access. +4. **`BaseEntity`** (NopDependencies) = root entity base with `int Id` and `IBaseEntity` interface. All nopCommerce entities inherit from this. Namespace: `Nop.Core`. +5. **`MgEntityBase`** = custom entity base that inherits `BaseEntity` and implements `IEntityInt` (from AyCode.Interfaces). Used for domain-specific entities and complex DTOs. +6. **Two DTO base strategies exist:** + - `ModelDtoBase` — for simple DTOs (e.g. `CustomerDto`). Manual `CopyEntityValuesToDto`/`CopyDtoValuesToEntity` overrides. Uses `Activator.CreateInstance()` in `CreateMainEntity()`. + - `MgEntityBase` + `IModelDtoBase` — for complex DTOs with LinqToDB `[Association]` navigations (e.g. `MgOrderDto`, `MgOrderItemDto`). Uses `PropertyHelper.CopyPublicValueTypeProperties()` for automatic value-type copy. +7. **`GenericAttribute`** = polymorphic key-value store. `KeyGroup` = owner type name, `EntityId` = owner ID, `Key` = attribute name, `Value` = string value. Use `GenericAttributeExtensions.GetValueOrNull()`/`GetValueOrDefault()`/`TryGetValue()` — never parse raw strings manually. ## Data Access Patterns -8. **`MgDbTableBase`** = repository base wrapping nopCommerce `EntityRepository`. Adds `GetAll()`, timestamp handling, and event publishing. -9. **`MgDtoDbTableBase`** = DTO-aware repository — handles DTO ↔ entity mapping on top of `MgDbTableBase`. -10. **`MgDbContextBase`** = EF Core context base for custom tables. +8. **`MgDbTableBase`** = repository base wrapping nopCommerce `EntityRepository`. Adds `GetAll()` (`IQueryable`), automatic timestamp handling (`ITimeStampCreated`/`ITimeStampModified`), virtual CRUD hooks (`OnInsert`/`OnUpdate`/`OnDelete`). +9. **`MgDtoDbTableBase`** = DTO-aware repository. **CRITICAL: All Delete methods throw — always use `DeleteMainEntityById(int id)`.** Event bridging: DTO insert/update events → main entity events. +10. **`MgDbContextBase`** = DB context base (NOT EF Core DbContext — wraps `INopDataProvider` and `IRepository` nopCommerce repos). Provides `Transaction`/`TransactionSafe`/`TransactionAsync`/`TransactionSafeAsync`. TransactionSafe variants use global `SemaphoreSlim` lock for serialized access. + +## Service Patterns +11. **`MgBackgroundServiceBase`** — inherits `Microsoft.Extensions.Hosting.BackgroundService`. Loop: `Task.Delay(interval)` → `OnExecuteAsync()`. Pause support. Per-iteration exception handling. **Uses nopCommerce `Nop.Services.Logging.ILogger`** (not Mango logger). +12. **`MgEventConsumerBase`** — subscribes to `EntityUpdatedEvent`, `EntityInsertedEvent`, `CustomerRegisteredEvent`, `OrderPlacedEvent`, `PageRenderingEvent`, `ProductSearchEvent`. Override relevant handlers. Built-in: `CheckAndUpdateProductManageInventoryMethodToManageStock()`. +13. **`NopLogWriter`** — AyCode → nopCommerce log bridge. Log level mapping: `Detail/Trace/Debug/Info` → `Information`; `Suggest/Warning` → `Warning`; `Error` → `Error`. Uses `TransactionScope(Suppress)` to avoid nesting. ## Conventions -11. **`Mg` prefix** for all custom types: `MgEntityBase`, `MgOrderDto`, `MgDbTableBase`, etc. -12. **No redundant code** — before writing new logic, search for existing methods. Reuse or extract shared logic. -13. **Keep all .md files in sync** — when you modify code, update any affected .md file in the same area. If you already read an .md file during your work and notice it contradicts the current code, fix the discrepancy — but do NOT proactively scan or open .md files just to check for issues. -14. All AyCode references are via **DLL** (not ProjectReference) — this is intentional. When a type is referenced but not defined in this library (e.g. `IEntityInt`, `IId`, `AcConst`, `ToonDescription`), look it up in `../../../../../../Aycode/Source/AyCode.Core/` source and docs. -15. **Documentation layering** — write `.md` documentation at the **defining layer** (where the code lives). Higher-layer `.md` files reference the base docs (e.g. `see AyCode.Core/docs/SIGNALR.md`) and document only project-specific overrides or extensions. Never duplicate base-layer descriptions in consumer-level docs. +14. **`Mg` prefix** for all custom types: `MgEntityBase`, `MgOrderDto`, `MgDbTableBase`, etc. +15. **No redundant code** — before writing new logic, check whether similar methods already exist in the current context. Reuse or extract shared logic. +16. **Keep all .md files in sync** — when you modify code and you know which .md file documents it, update that .md file too. If you notice a contradiction between code and an .md file, automatically update the .md to match the code (code is the source of truth). When fixing a reference, check other .md files that may share the same broken reference. If the root cause is at a lower layer, fix it there first. During code review, if you find useful behavior or conventions not yet documented, briefly suggest what to document and where — but do not add new content without approval. +17. All AyCode references are via **DLL** (not ProjectReference) — this is intentional. Types not defined in this library (e.g. `IEntityInt`, `IId`, `AcConst`, `ToonDescription`) likely live in AyCode.Core. DLL HintPaths in .csproj point to the correct relative location. +18. **Documentation layering** — write `.md` documentation at the **defining layer** (where the code lives). Higher-layer `.md` files reference AyCode.Core base docs using the form `AyCode.Core/{Project}/docs/FILENAME.md` (resolved via rule 17 base path). Document only project-specific overrides or extensions. Never duplicate base-layer descriptions in consumer-level docs. +19. **LinqToDB associations** — use `[Association(ThisKey = nameof(FK), OtherKey = nameof(Target.Id), CanBeNull = true/false)]` for navigation properties in DTOs. +20. **Timestamp auto-management** — entities with `ITimeStampCreated` or `ITimeStampModified` get automatic UTC timestamps in `MgDbTableBase` CRUD hooks. Don't set these manually. ## Reference Modes - **Full stack** (ProjectReference, all 3 libraries) — for nopCommerce plugins needing entity access, data layer, and services. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d8f5167 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,10 @@ +# Mango.Nop Libraries — Claude Code Instructions + +Before writing any code, read these files: +1. `.github/copilot-instructions.md` — Domain rules (single source of truth) +2. `docs/GLOSSARY.md` — Entity/DTO, data access, and service terminology +3. The relevant project's `README.md` for folder-specific context + +These libraries depend on **AyCode.Core** (DLL references — see HintPaths in .csproj for relative location). When a type is referenced but not defined here (e.g. `IEntityInt`, `IAcLoggerBase`, `AcBinarySerializer`, `ToonDescription`), look it up in AyCode.Core source. For core framework rules see `../AyCode.Core/CLAUDE.md`. + +When modifying code, update the corresponding README.md if it becomes out of sync with the code. diff --git a/Mango.Nop.Core/Mango.Nop.Core.csproj b/Mango.Nop.Core/Mango.Nop.Core.csproj index fe1eb5d..4aa16cd 100644 --- a/Mango.Nop.Core/Mango.Nop.Core.csproj +++ b/Mango.Nop.Core/Mango.Nop.Core.csproj @@ -45,4 +45,9 @@ + + + + + diff --git a/Mango.Nop.Core/README.md b/Mango.Nop.Core/README.md index acd39d7..96cfe30 100644 --- a/Mango.Nop.Core/README.md +++ b/Mango.Nop.Core/README.md @@ -1,41 +1,92 @@ # Mango.Nop.Core -Shared domain library containing entities, DTOs, interfaces, and nopCommerce entity mirrors. **net9.0**. +@project { + type = "framework" + own-dep-projects = [ + "AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)" + ] +} + +Shared domain library containing entities, DTOs, interfaces, and nopCommerce entity mirrors. **net9.0**. Zero nopCommerce runtime dependency. + +## Documentation + +| Document | Topic | +|---|---| +| `DTOS.md` | DTO system — two mapping strategies, all DTO types, GenericAttribute typed access | +| `NOP_DEPENDENCIES.md` | NopDependencies pattern — mirror copies, namespace rules, file list | ## Folder Structure | Folder | Purpose | |---|---| -| `Dtos/` | DTO classes shared across consumers: `MgOrderDto`, `MgOrderItemDto`, `MgProductDto`, `MgGenericAttributeDto`, `MgStockQuantityHistoryDto`, `CustomerDto`, `ModelDtoBase` | -| `Entities/` | Custom entities: `MgEntityBase`, `MgStockTaking`, `MgStockTakingItem` | -| `Extensions/` | Extension methods for nopCommerce `BaseEntity` collections and `GenericAttribute` | -| `Interfaces/` | DTO interfaces (`IMgOrderDto`, `IMgProductDto`, etc.), soft-delete interfaces, foreign key markers | -| `Loggers/` | `ILogger` / `Logger` — logging abstraction | -| `Models/` | Login request/response models (`MgLoginModelRequest`, `MgLoginModelResponse`) | -| `NopDependencies/` | Mirror copies of nopCommerce entity classes — `BaseEntity`, `Customer`, `Order`, `OrderItem`, `Product`, `GenericAttribute`, enums (`OrderStatus`, `PaymentStatus`, etc.) | +| `Dtos/` | DTO classes shared across consumers | +| `Entities/` | Custom domain entities | +| `Extensions/` | Extension methods for `BaseEntity` collections and `GenericAttribute` | +| `Interfaces/` | DTO interfaces, soft-delete, foreign key markers | +| `Loggers/` | `ILogger` / `Logger` — logging abstraction wrapping AyCode logger | +| `Models/` | Login request/response models | +| `NopDependencies/` | Mirror copies of nopCommerce entity classes (same namespaces as originals) | | `Services/` | `IMgLockService` interface | -| `Utils/` | `CommonHelper2` — utility methods | +| `Utils/` | `CommonHelper2` — email validation, type conversion utilities | -## Key Types +## Entity Hierarchy -### DTOs (Dtos/) -- **`MgOrderDto`** — Order DTO used across SignalR communication -- **`MgOrderItemDto`** — Order item DTO -- **`MgProductDto`** — Product DTO with GenericAttribute-based properties -- **`MgGenericAttributeDto`** — GenericAttribute DTO -- **`ModelDtoBase`** / `IModelDtoBase` — base class for all DTOs +``` +Nop.Core.BaseEntity (NopDependencies/, implements IBaseEntity) + +-- MgEntityBase (Entities/, implements IEntityInt from AyCode) + +-- MgOrderDto + +-- MgOrderItemDto + +-- MgProductDto + +-- MgStockQuantityHistoryDto + +-- MgStockTaking + +-- MgStockTakingItem +``` -### nopCommerce Mirrors (NopDependencies/) -Mirror copies of nopCommerce entities so that `Mango.Nop.Core` can be referenced without pulling in the full nopCommerce dependency chain. Includes: -- `BaseEntity` — root entity base -- `Customer`, `CustomerRole`, `Order`, `OrderItem`, `Product`, `GenericAttribute`, `StockQuantityHistory` -- Enums: `OrderStatus`, `PaymentStatus`, `ShippingStatus`, `ProductType`, etc. +## Key Types (not in docs/) -### Entities (Entities/) -- **`MgEntityBase`** — base for all custom Mango entities -- **`MgStockTaking`**, **`MgStockTakingItem`** — stocktaking entities +### Entities + +| Type | Key features | +|---|---| +| `MgEntityBase` | Inherits `BaseEntity`, implements `IEntityInt`. `ToString()` -> `"{TypeName}; Id: {Id}"` | +| `MgStockTaking` | `StartDateTime`, `IsClosed`, `List` navigation. Implements `ITimeStampInfo` | +| `MgStockTakingItem` | `StockTakingId`, `ProductId`, `IsMeasured`, stock quantities. Navigations to parent and product | + +### Extensions + +| Method | Purpose | +|---|---| +| `UpdateBaseEntityCollection` | Add/update/remove entities in a list by Id match | + +### Loggers + +Extends AyCode logging infrastructure — for base types see `AyCode.Core/docs/LOGGING.md`. + +| Type | Inherits | Purpose | +|---|---|---| +| `ILogger` | `IAcLoggerBase` | Mango-level logger interface | +| `ILogger` | `ILogger` | Generic category logger interface | +| `Logger` | `AcLoggerBase` | Logger implementation with `IAcLogWriterBase[]` writers | +| `Logger` | `Logger` | Typed logger — category name from `typeof(TCategory).Name` | + +### Models + +| Type | Purpose | +|---|---| +| `MgLoginModelRequest` | `Email` + `Password` for SignalR/API login | +| `MgLoginModelResponse` | `CustomerDto?` + `ErrorMessage`. Computed `IsSuccesLogin` | + +### Other + +| Type | Purpose | +|---|---| +| `NopCommonConst` | Abstract const class inheriting `AcConst` (placeholder) | +| `CommonHelper2` | Email validation, IP validation, type conversion (`To`), string utilities | ## Dependencies -- `linq2db` — data access -- `MessagePack` — binary serialization + +- `linq2db` — data access, `[Association]` / `[Table]` attributes - `Microsoft.AspNetCore.Mvc.NewtonsoftJson` — JSON serialization +- Binary serialization: `AcBinarySerializer` (see `AyCode.Core/docs/BINARY_FORMAT.md`). SignalR transport: `AcSignalR` (see `AyCode.Core/AyCode.Services/docs/SIGNALR.md`). +- AyCode DLL references: `AyCode.Interfaces`, `AyCode.Core`, `AyCode.Utils`, `AyCode.Entities` (types only, no runtime nopCommerce dependency) diff --git a/Mango.Nop.Core/docs/DTOS.md b/Mango.Nop.Core/docs/DTOS.md new file mode 100644 index 0000000..020c400 --- /dev/null +++ b/Mango.Nop.Core/docs/DTOS.md @@ -0,0 +1,74 @@ +# DTO System + +> Part of `Mango.Nop.Core`. See `Mango.Nop.Core/README.md` for project overview. + +## Two Mapping Strategies + +### Strategy 1: `ModelDtoBase` (simple DTOs) + +Used by: `CustomerDto`, `MgGenericAttributeDto` + +``` +ModelDtoBase → ModelDtoBase → CustomerDto +``` + +- Manual `CopyEntityValuesToDto`/`CopyDtoValuesToEntity` overrides +- `CreateMainEntity()` uses `Activator.CreateInstance()` +- No LinqToDB associations + +### Strategy 2: `MgEntityBase` + `IModelDtoBase` (complex DTOs) + +Used by: `MgOrderDto`, `MgOrderItemDto`, `MgStockQuantityHistoryDto` + +``` +BaseEntity → MgEntityBase → MgOrderDto : IModelDtoBase +``` + +- Uses `PropertyHelper.CopyPublicValueTypeProperties()` for bulk value-type copy +- LinqToDB `[Association]` navigation properties +- Generic type parameters for child DTOs + +### Strategy 3: Entity inheritance (MgProductDto) + +``` +BaseEntity → MgEntityBase → MgProductDto : IMgProductDto +``` + +- No `IModelDtoBase` (entity mapping methods are commented out) +- Direct property declarations mirroring `Product` fields + +## DTO Types + +| Type | Generic params | Maps to entity | Key features | +|---|---|---|---| +| `ModelDtoBase` | — | — | Abstract base, `int Id` only | +| `ModelDtoBase` | `TMainEntity : BaseEntity` | `TMainEntity` | `CreateMainEntity()`, `CopyEntityValuesToDto()`, `CopyDtoValuesToEntity()` | +| `MgOrderDto` | `TOrderItemDto : IMgOrderItemDto`, `TProductDto : IMgProductDto` | `Order` | Has `Customer`, `List`, `List` navigations. Enum wrappers: `OrderStatus`, `ShippingStatus`, `PaymentStatus` | +| `MgOrderItemDto` | `TProductDto : IMgProductDto` | `OrderItem` | Has `TProductDto?` navigation. Computed `ProductName` property | +| `MgProductDto` | — | *(commented out)* | Base product DTO. Name, Price, StockQuantity, dimensions. No entity mapping methods (currently commented out) | +| `MgGenericAttributeDto` | — | `GenericAttribute` | Inherits `GenericAttribute` directly (not `ModelDtoBase`). Full bidirectional mapping | +| `MgStockQuantityHistoryDto` | `TProductDto : IMgProductDto` | `StockQuantityHistory` | Has `TProductDto` navigation. Mapping methods `NotImplementedException` (stub) | +| `CustomerDto` | — | `Customer` | `Username`, `Email`, `FirstName`, `LastName`, `FullName` computed. Uses `[AcBinarySerializable]` and LinqToDB `[Table]` | + +## DTO Interfaces + +| Interface | Extends | Purpose | +|---|---|---| +| `IModelDtoBase` | `IEntityInt`, `IModelDtoBaseEmpty` | Marker for all DTOs with `int Id` | +| `IModelDtoBase` | `IModelDtoBase` | Bidirectional mapping contract: `CreateMainEntity()`, `CopyDtoValuesToEntity()`, `CopyEntityValuesToDto()` | +| `IMgOrderDto` | `IEntityInt`, `ISoftDeletedEntity` | Order DTO contract with navigation properties and initialization methods | +| `IMgOrderItemDto` | `IEntityInt` | Order item DTO contract with product navigation | +| `IMgProductDto` | `IEntityInt`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `ISoftDeletedEntity` | Product DTO contract | + +## GenericAttribute Typed Access + +`GenericAttributeExtensions` provides typed access to nopCommerce's polymorphic key-value store: + +| Method | Signature | Purpose | +|---|---|---| +| `GetValueOrNull` | `(IEnumerable, string key) -> TValue?` | Get typed value, returns null if not found | +| `GetValueOrDefault` | `(IEnumerable, string key, TValue default) -> TValue` | Same with default fallback | +| `TryGetValue` | `(IEnumerable, string key, out TValue?) -> bool` | Try-pattern for GA value | +| `AddNewGenericAttribute` | `(ICollection, ...) -> GenericAttribute` | Create and add new GA with UTC timestamp | + +**Rule:** Always use these extension methods — never parse `GenericAttribute.Value` strings manually. diff --git a/Mango.Nop.Core/docs/NOP_DEPENDENCIES.md b/Mango.Nop.Core/docs/NOP_DEPENDENCIES.md new file mode 100644 index 0000000..82c9144 --- /dev/null +++ b/Mango.Nop.Core/docs/NOP_DEPENDENCIES.md @@ -0,0 +1,46 @@ +# NopDependencies Pattern + +> Part of `Mango.Nop.Core`. See `Mango.Nop.Core/README.md` for project overview. + +## Why + +`Mango.Nop.Core` has **zero nopCommerce runtime dependency**. This allows it to be referenced by projects that don't have the full nopCommerce stack (e.g. Blazor/MAUI clients that only need DTOs and interfaces). + +To achieve this, `NopDependencies/` contains **mirror copies** of nopCommerce entity classes with the **same namespace** as the originals: + +```csharp +// In NopDependencies/BaseEntity.cs — same namespace as nopCommerce +namespace Nop.Core; +public abstract partial class BaseEntity : IBaseEntity +{ + public int Id { get; set; } +} +``` + +At compile time, projects referencing only `Mango.Nop.Core` get these mirror types. Projects with the full nopCommerce stack get the real types — they are type-compatible because they share the same namespace and shape. + +## Rules + +- **DO NOT modify** files in `NopDependencies/` unless the nopCommerce version changes +- **DO NOT change namespaces** — they must match the original nopCommerce types exactly +- All mirror classes are `partial` — they can be extended but should not be modified directly + +## File List + +| File | Namespace | Type | +|---|---|---| +| `BaseEntity.cs` | `Nop.Core` | `BaseEntity` (abstract, `IBaseEntity`) + `IBaseEntity` interface | +| `Catalogs/Customer.cs` | `Nop.Core.Domain.Customers` | `Customer` | +| `Catalogs/CustomerRole.cs` | `Nop.Core.Domain.Customers` | `CustomerRole` | +| `Catalogs/Order.cs` | `Nop.Core.Domain.Orders` | `Order` | +| `Catalogs/OrderItem.cs` | `Nop.Core.Domain.Orders` | `OrderItem` | +| `Catalogs/OrderNote.cs` | `Nop.Core.Domain.Orders` | `OrderNote` | +| `Catalogs/Product.cs` | `Nop.Core.Domain.Catalog` | `Product` | +| `Catalogs/GenericAttribute.cs` | `Nop.Core.Domain.Common` | `GenericAttribute` | +| `Catalogs/StockQuantityHistory.cs` | `Nop.Core.Domain.Catalog` | `StockQuantityHistory` | +| `Catalogs/DiscountMapping.cs` | `Nop.Core.Domain.Catalog` | `DiscountMapping` | +| `Catalogs/DiscountProductMapping.cs` | `Nop.Core.Domain.Catalog` | `DiscountProductMapping` | + +**Enums:** `OrderStatus`, `PaymentStatus`, `ShippingStatus`, `ProductType`, `ManageInventoryMethod`, `BackorderMode`, `LowStockActivity`, `GiftCardType`, `RentalPricePeriod`, `RecurringProductCyclePeriod`, `DownloadActivationType`, `TaxDisplayType`, `VatNumberStatus` + +**Interfaces:** `ISoftDeletedEntity`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `IDiscountSupported` diff --git a/Mango.Nop.Data/Mango.Nop.Data.csproj b/Mango.Nop.Data/Mango.Nop.Data.csproj index dc99870..7faccd2 100644 --- a/Mango.Nop.Data/Mango.Nop.Data.csproj +++ b/Mango.Nop.Data/Mango.Nop.Data.csproj @@ -44,4 +44,10 @@ ..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Utils.dll + + + + + + diff --git a/Mango.Nop.Data/README.md b/Mango.Nop.Data/README.md index a51824b..f7bd2ef 100644 --- a/Mango.Nop.Data/README.md +++ b/Mango.Nop.Data/README.md @@ -1,7 +1,21 @@ # Mango.Nop.Data +@project { + type = "framework" + own-dep-projects = [ + "AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)" + ] +} + Data access layer with repository base classes and DB context abstractions. **net9.0**. +## Documentation + +| Document | Topic | +|---|---| +| `REPOSITORIES.md` | MgDbTableBase, MgDtoDbTableBase — CRUD hooks, timestamps, delete rules, event bridging | +| `TRANSACTIONS.md` | MgDbContextBase — 4 transaction methods, lock strategy, callback contract | + ## Folder Structure | Folder | Purpose | @@ -9,15 +23,38 @@ Data access layer with repository base classes and DB context abstractions. **ne | `Interfaces/` | Repository interfaces: `IMgDalBase`, `IMgDbContextBase`, `IMgDbTableBase` | | `Repositories/` | Repository base implementations: `MgDalBase`, `MgDbContextBase`, `MgDbTableBase`, `MgDtoDbTableBase` | -## Key Types +## Inheritance Chains -- **`MgDalBase`** — Data Access Layer base class -- **`MgDbContextBase`** — Database context base for nopCommerce integration -- **`MgDbTableBase`** — Table-level repository base (entity → DB operations) -- **`MgDtoDbTableBase`** — DTO-aware table base — handles DTO ↔ entity mapping +### Repository hierarchy +``` +nopCommerce EntityRepository (Nop.Data) + +-- MgDbTableBase + +-- MgDtoDbTableBase +``` + +### Context hierarchy +``` +MgDbContextBase (abstract, implements IMgDbContextBase) + +-- [Consumer DbContexts in plugins] +``` + +### DAL hierarchy +``` +MgDalBase (implements IMgDalBase) + +-- [Consumer DALs in plugins] +``` + +## Interfaces + +| Interface | Purpose | +|---|---| +| `IMgDbTableBase` | Marker interface for repository classes | +| `IMgDbContextBase` | Contract: `Logger`, `DataProvider`, `Orders`, `Products`, 4 transaction methods | +| `IMgDalBase` | `Name` property | +| `IMgDalBase` | `Context`, `MutextLock` — typed access to DB context | ## Dependencies - `Mango.Nop.Core` (ProjectReference) - `Nop.Core`, `Nop.Data` (nopCommerce ProjectReferences) -- `MessagePack`, `Microsoft.AspNetCore.Mvc.NewtonsoftJson` +- `Microsoft.AspNetCore.Mvc.NewtonsoftJson` diff --git a/Mango.Nop.Data/docs/REPOSITORIES.md b/Mango.Nop.Data/docs/REPOSITORIES.md new file mode 100644 index 0000000..0e1cea4 --- /dev/null +++ b/Mango.Nop.Data/docs/REPOSITORIES.md @@ -0,0 +1,42 @@ +# Repository Pattern + +> Part of `Mango.Nop.Data`. See `Mango.Nop.Data/README.md` for project overview. +> For transaction patterns see `docs/TRANSACTIONS.md`. + +## MgDbTableBase\ + +Repository base wrapping nopCommerce `EntityRepository`. + +**Constructor:** +```csharp +MgDbTableBase(IEventPublisher, INopDataProvider, IShortTermCacheManager, IStaticCacheManager, AppSettings) +``` + +### Features + +| Feature | Detail | +|---|---| +| `GetAll()` | Returns `IQueryable` from `Table` property | +| Automatic timestamps | `ITimeStampCreated.Created` set on insert; `ITimeStampModified.Modified` set on insert/update | +| CRUD hooks | `OnInsert(entity)`, `OnUpdate(entity)`, `OnDelete(entity)` — virtual, overridable | +| Cache clear | `OnUpdate` clears `IStaticCacheManager` (currently clears all — TODO noted in code) | +| All CRUD overrides | Overrides all sync/async `Insert`, `Update`, `Delete` methods to call hooks before `base.*` | +| `DeleteAsync(int entityId)` | Convenience: load by id then delete | +| `DeleteAsync(predicate, bool publishEvent)` | When `publishEvent=true`, loads entities first then deletes with events | + +## MgDtoDbTableBase\ + +DTO-aware repository for when the DTO entity (`TDtoEntity`) maps to a different main nopCommerce entity (`TMainEntity`). This is needed because LinqToDB tables are registered for DTOs, but nopCommerce events must fire on the main entity type. + +### Features + +| Feature | Detail | +|---|---| +| `GetMainEntityById(id)` | Loads `TMainEntity` from `INopDataProvider.GetTable()` | +| `DeleteMainEntityById(id)` | Deletes the **main entity** (not the DTO) and publishes `EntityDeletedEvent` | +| Delete overrides | **All Delete methods throw** — forces callers to use `DeleteMainEntityById()` instead | +| Event bridging | `EntityInsertedEvent` -> loads main entity -> publishes `EntityInsertedEvent` | +| Event bridging | `EntityUpdatedEvent` -> loads main entity -> publishes `EntityUpdatedEvent` | +| Event bridging | `EntityDeletedEvent` -> **throws** (must use `DeleteMainEntityById`) | + +**Critical rule:** Never call `Delete` on a `MgDtoDbTableBase` repository directly. Always use `DeleteMainEntityById(int id)`. diff --git a/Mango.Nop.Data/docs/TRANSACTIONS.md b/Mango.Nop.Data/docs/TRANSACTIONS.md new file mode 100644 index 0000000..8aaf61a --- /dev/null +++ b/Mango.Nop.Data/docs/TRANSACTIONS.md @@ -0,0 +1,53 @@ +# Transaction Pattern + +> Part of `Mango.Nop.Data`. See `Mango.Nop.Data/README.md` for project overview. +> For repository base classes see `docs/REPOSITORIES.md`. + +## MgDbContextBase + +Abstract database context base. NOT an EF Core DbContext — wraps `INopDataProvider` and `IRepository` nopCommerce repos. + +**Constructor:** +```csharp +MgDbContextBase(IRepository, IRepository, IRepository, INopDataProvider, IMgLockService, ILogger) +``` + +### Standard Repositories + +| Property | Type | +|---|---| +| `Orders` | `IRepository` | +| `OrderItems` | `IRepository` | +| `Products` | `IRepository` | +| `DataProvider` | `INopDataProvider` (LinqToDB raw queries) | +| `Logger` | Mango `ILogger` | +| `LockService` | `IMgLockService` (global `SemaphoreSlim`) | + +### 4 Transaction Methods + +| Method | Lock | Async | +|---|---|---| +| `Transaction(callback)` | No | No | +| `TransactionSafe(callback)` | `SemaphoreSlim` | No | +| `TransactionAsync(callback)` | No | Yes (thread pool) | +| `TransactionSafeAsync(callback)` | `SemaphoreSlim` | Yes (thread pool) | + +**Callback contract:** `Func)>` — return `true` to commit (`Complete()`), `false` to rollback. + +**Isolation level:** `ReadCommitted` + +**Error handling:** Catches exceptions, logs, returns `false` (unless `throwException = true`). + +**TransactionSafe variants:** Use `LockService.SemaphoreSlim` for global serialization. Use for order creation, stock adjustment — any operation where concurrent modifications would corrupt data. + +**Async variants:** Run on thread pool via `TaskHelper.ToThreadPoolTask()`. + +## MgDalBase\ + +Data Access Layer orchestrator. Thin wrapper exposing: + +| Property | Type | Purpose | +|---|---|---| +| `Name` | `string` | DAL instance name | +| `Context` | `TDbContext` | The DB context (typed) | +| `MutextLock` | `Mutex` | Cross-process locking | diff --git a/Mango.Nop.Services/Mango.Nop.Services.csproj b/Mango.Nop.Services/Mango.Nop.Services.csproj index 92d46d4..08ae823 100644 --- a/Mango.Nop.Services/Mango.Nop.Services.csproj +++ b/Mango.Nop.Services/Mango.Nop.Services.csproj @@ -47,4 +47,10 @@ + + + + + + diff --git a/Mango.Nop.Services/README.md b/Mango.Nop.Services/README.md index a98fd3e..263cdc9 100644 --- a/Mango.Nop.Services/README.md +++ b/Mango.Nop.Services/README.md @@ -1,17 +1,27 @@ # Mango.Nop.Services -Service base classes for nopCommerce plugin development — background tasks, session management, events, locking. **net9.0**. +@project { + type = "framework" + own-dep-projects = [ + "AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)" + ] +} -## Key Types +Service base classes for nopCommerce plugin development — background tasks, session management, events, locking, logging. **net9.0**. -| Type | Purpose | +## Documentation + +| Document | Topic | |---|---| -| `MgBackgroundServiceBase` / `IMgBackgroundService` | Base for hosted background services in nopCommerce | -| `MgSessionServiceBase` / `IMgSessionService` | Session management base — user session state | -| `MgSessionItemBase` / `IMgSessionItem` | Individual session item base | -| `MgEventConsumerBase` | Base for nopCommerce event consumers (entity insert/update/delete) | -| `MgLockServiceBase` | Distributed lock service base | -| `NopLogWriter` | Logging bridge — writes to nopCommerce log system | +| `SERVICES.md` | MgBackgroundServiceBase, MgSessionServiceBase, MgEventConsumerBase, MgLockServiceBase | +| `LOGGING_BRIDGE.md` | NopLogWriter — AyCode-to-nopCommerce log bridge, TransactionScope(Suppress) | + +## Folder Structure + +| Folder | Purpose | +|---|---| +| `Loggers/` | `NopLogWriter`, `NopLoggerMsSqlNopDataProvider` — AyCode -> nopCommerce log bridge | +| *(root)* | `MgBackgroundServiceBase`, `MgSessionServiceBase`, `MgEventConsumerBase`, `MgLockServiceBase`, interfaces | ## Dependencies diff --git a/Mango.Nop.Services/docs/LOGGING_BRIDGE.md b/Mango.Nop.Services/docs/LOGGING_BRIDGE.md new file mode 100644 index 0000000..8932b99 --- /dev/null +++ b/Mango.Nop.Services/docs/LOGGING_BRIDGE.md @@ -0,0 +1,40 @@ +# NopLogWriter — Logging Bridge + +> Part of `Mango.Nop.Services`. See `Mango.Nop.Services/README.md` for project overview. +> For AyCode base logging types (`AcLogItemWriterBase`, `AcLogItem`, log levels) see `AyCode.Core/docs/LOGGING.md`. + +## Overview + +Bridges AyCode logging to nopCommerce's `Nop.Core.Domain.Logging.Log` table via direct DB insert. + +## NopLogWriter + +| Feature | Detail | +|---|---| +| Inherits | `AcLogItemWriterBase` (AyCode.Core) | +| Log level mapping | AyCode `Detail/Trace/Debug/Info` -> nopCommerce `Information`; `Suggest/Warning` -> `Warning`; `Error` -> `Error` | +| Direct DB insert | Uses `NopLoggerMsSqlNopDataProvider` with `TransactionScope(Suppress)` to avoid transaction conflicts | + +**Constructor:** `(INopLoggerMsSqlNopDataProvider, CommonSettings, CustomerSettings, IWebHelper, Nop.Services.Logging.ILogger, string? categoryName)` + +## NopLoggerMsSqlNopDataProvider + +Extends `MsSqlNopDataProvider`. Provides isolated DB access for log writes. + +| Method | Purpose | +|---|---| +| `InsertLogItem()` | Sync insert with own `TransactionScope(Suppress, ReadUncommitted)` | +| `InsertLogItemAsync()` | Async insert with own `TransactionScope(Suppress, ReadUncommitted)` | + +The `TransactionScope(Suppress)` is critical — without it, log writes would participate in the caller's transaction, causing nesting conflicts and potential deadlocks. + +## Integration Point + +``` +Application code -> Mango.Nop.Core.Loggers.ILogger (extends IAcLoggerBase) + -> Logger (extends AcLoggerBase, delegates to IAcLogWriterBase[]) + -> NopLogWriter -> Nop Log table (direct SQL insert) + -> [Other AyCode log writers: console, file, SignalR, etc.] +``` + +**Exception:** `MgBackgroundServiceBase` uses `Nop.Services.Logging.ILogger` directly (nopCommerce logger), not the Mango wrapper. diff --git a/Mango.Nop.Services/docs/SERVICES.md b/Mango.Nop.Services/docs/SERVICES.md new file mode 100644 index 0000000..f4123d1 --- /dev/null +++ b/Mango.Nop.Services/docs/SERVICES.md @@ -0,0 +1,71 @@ +# Service Base Classes + +> Part of `Mango.Nop.Services`. See `Mango.Nop.Services/README.md` for project overview. +> For logging bridge see `docs/LOGGING_BRIDGE.md`. + +## MgBackgroundServiceBase + +Abstract hosted background service. Inherits `Microsoft.Extensions.Hosting.BackgroundService`. + +**Constructor:** `(ILogger, IServiceProvider, int executeIntervalMs)` + +| Feature | Detail | +|---|---| +| Loop pattern | `ExecuteAsync` loops: `Task.Delay(ExecuteIntervalMs)` -> `OnExecuteAsync()`, with pause support | +| `OnExecuteAsync(CancellationToken)` | Abstract — subclass implements the actual work | +| `Pause(bool)` | Pauses/resumes the loop without stopping the service | +| `ExecuteIntervalMs` | Configurable interval between iterations | +| Exception handling | Catches and logs errors per iteration, never crashes the loop | +| Logging | Uses nopCommerce `Nop.Services.Logging.ILogger` (**not** `Mango.Nop.Core.Loggers.ILogger`) | + +**Interface:** `IMgBackgroundService : IHostedService, IDisposable` + +## MgSessionServiceBase\ + +In-memory session management using `ConcurrentDictionary`. + +| Method | Signature | Purpose | +|---|---|---| +| `GetOrCreateSessionItem` | `(string sessionId) -> TSessionItem?` | Get existing or create new via `Activator.CreateInstance` | +| `TryAddSessionItem` | `(TSessionItem) -> bool` | Add if not exists, throws if duplicate | +| `TryGetSessionItem` | `(string sessionId, out TSessionItem) -> bool` | Try-pattern lookup | +| `TryRemoveSessionItem` | `(string sessionId, out TSessionItem) -> bool` | Remove and return | +| `TryGetSessionItemBySignlaRConnectionId` | `(string connectionId, out TSessionItem?) -> bool` | Find session by SignalR connection | +| `Count` | `() -> int` | Active session count | + +**Interface:** `IMgSessionService where TSessionItem : IMgSessionItem` + +## MgSessionItemBase + +Base session item. Primary constructor: `(string sessionKey)`. + +| Property | Type | Purpose | +|---|---|---| +| `SessionId` | `string` | Unique session identifier | +| `SignaRConnectionId` | `string?` | Associated SignalR connection ID | +| `RequestCount` | `int` | Request counter | + +**Interface:** `IMgSessionItem` — `SessionId`, `SignaRConnectionId`, `RequestCount` + +## MgEventConsumerBase + +Abstract nopCommerce event consumer. Subscribes to: + +| Event | Handler | +|---|---| +| `EntityUpdatedEvent` | `HandleEventAsync(...)` — virtual, empty default | +| `EntityInsertedEvent` | `HandleEventAsync(...)` — virtual, empty default | +| `CustomerRegisteredEvent` | `HandleEventAsync(...)` — virtual, empty default | +| `OrderPlacedEvent` | `HandleEventAsync(...)` — virtual, empty default | +| `PageRenderingEvent` | `HandleEventAsync(...)` — virtual, empty default | +| `ProductSearchEvent` | `HandleEventAsync(...)` — virtual, empty default | + +Built-in helper: `CheckAndUpdateProductManageInventoryMethodToManageStock(Product)` — ensures product has `ManageStock` inventory method. + +**Constructor:** `(IMgDbContextBase ctx, IHttpContextAccessor, IEnumerable logWriters)` + +## MgLockServiceBase + +Simple in-process lock using `SemaphoreSlim(1)`. Implements `IMgLockService` (defined in `Mango.Nop.Core.Services`). + +Used by `MgDbContextBase.TransactionSafe*` variants for global serialization. diff --git a/README.md b/README.md index 23fdb65..86caa85 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,43 @@ Shared nopCommerce extension libraries providing domain entities, DTOs, data acc | Project | Purpose | Key Types | |---|---|---| -| [Mango.Nop.Core](Mango.Nop.Core/README.md) | Domain entities, DTOs, interfaces, nopCommerce entity mirrors | `MgOrderDto`, `MgProductDto`, `MgEntityBase`, `GenericAttribute`, `BaseEntity` | -| [Mango.Nop.Data](Mango.Nop.Data/README.md) | Data access layer — repository base classes, DB context | `MgDalBase`, `MgDbContextBase`, `MgDbTableBase`, `MgDtoDbTableBase` | -| [Mango.Nop.Services](Mango.Nop.Services/README.md) | Service base classes — background services, session, events, locking | `MgBackgroundServiceBase`, `MgSessionServiceBase`, `MgEventConsumerBase`, `NopLogWriter` | +| `Mango.Nop.Core` | Domain entities, DTOs, interfaces, nopCommerce entity mirrors (zero nopCommerce runtime dep) | `MgEntityBase`, `ModelDtoBase`, `MgOrderDto<,>`, `MgOrderItemDto<>`, `MgProductDto`, `CustomerDto`, `MgGenericAttributeDto`, `MgStockTaking<>`, `GenericAttributeExtensions` | +| `Mango.Nop.Data` | Data access layer — repository base classes, DB context, transactions | `MgDbTableBase`, `MgDtoDbTableBase<,>`, `MgDbContextBase`, `MgDalBase` | +| `Mango.Nop.Services` | Service base classes — background services, session, events, locking, logging | `MgBackgroundServiceBase`, `MgSessionServiceBase`, `MgEventConsumerBase`, `MgLockServiceBase`, `NopLogWriter` | ## Dependency Graph -See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for full dependency graph and project roles. +See `docs/ARCHITECTURE.md` for full dependency graph, project roles, DTO mapping strategies, and transaction patterns. -`Mango.Nop.Core` has zero nopCommerce runtime dependency (uses mirror copies in `NopDependencies/`). `Mango.Nop.Data` and `.Services` depend on nopCommerce. +``` +AyCode.Core (DLL references) + ↑ +Mango.Nop.Core (net9.0, zero nopCommerce runtime dependency) + ↑ +Mango.Nop.Data (net9.0) → Nop.Core, Nop.Data + ↑ +Mango.Nop.Services (net9.0) → Nop.Core, Nop.Data, Nop.Services, Nop.Web.Framework +``` ## Reference Modes - **Full stack** (ProjectReference, all 3 libraries) — for nopCommerce plugins that need entity access, data layer, and services. - **Types only** (DLL reference, `Mango.Nop.Core` only) — for projects that only need DTOs, entities, and interfaces without the nopCommerce runtime dependency. + +## Documentation Map + +| File | Purpose | +|---|---| +| `.github/copilot-instructions.md` | **Domain rules** — single source of truth for all conventions | +| `docs/ARCHITECTURE.md` | Dependency graph, project roles, DTO strategies, transaction patterns, logging architecture | +| `docs/CONVENTIONS.md` | Naming, patterns, project boundaries, AyCode integration points | +| `docs/GLOSSARY.md` | Term definitions — entity/DTO system, data access, services, AyCode types | +| `Mango.Nop.Core/README.md` | Core project overview — entity hierarchy, loggers, extensions, models | +| `Mango.Nop.Core/docs/DTOS.md` | DTO system — two mapping strategies, all DTO types, GenericAttribute access | +| `Mango.Nop.Core/docs/NOP_DEPENDENCIES.md` | NopDependencies pattern — mirror copies, namespace rules, file list | +| `Mango.Nop.Data/README.md` | Data project overview — repository and context hierarchy, interfaces | +| `Mango.Nop.Data/docs/REPOSITORIES.md` | MgDbTableBase, MgDtoDbTableBase — CRUD hooks, timestamps, delete rules | +| `Mango.Nop.Data/docs/TRANSACTIONS.md` | MgDbContextBase — 4 transaction methods, lock strategy, callback contract | +| `Mango.Nop.Services/README.md` | Services project overview | +| `Mango.Nop.Services/docs/SERVICES.md` | MgBackgroundServiceBase, MgSessionServiceBase, MgEventConsumerBase, MgLockServiceBase | +| `Mango.Nop.Services/docs/LOGGING_BRIDGE.md` | NopLogWriter — AyCode-to-nopCommerce log bridge | diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 63d50db..a06ca16 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -18,28 +18,28 @@ Mango.Nop.Services (net9.0) → Nop.Core, Nop.Data, Nop.Services, Nop.Web.Framew ### Mango.Nop.Core — Domain Layer Zero nopCommerce runtime dependency. Contains: -- **DTOs** (`Dtos/`) — `ModelDtoBase` with bidirectional entity mapping (`CopyEntityValuesToDto`, `CreateMainEntity`) +- **DTOs** (`Dtos/`) — `ModelDtoBase` with bidirectional entity mapping (`CopyEntityValuesToDto`, `CopyDtoValuesToEntity`, `CreateMainEntity`) - **Entities** (`Entities/`) — `MgEntityBase` (inherits `BaseEntity`, implements `IEntityInt`) - **NopDependencies** (`NopDependencies/`) — mirror copies of nopCommerce entities (`BaseEntity`, `Customer`, `Order`, `Product`, `GenericAttribute`, enums). Allows referencing without full nopCommerce dependency chain. - **Extensions** (`Extensions/`) — `GenericAttributeExtensions` for typed attribute access, `CollectionExtensionsNopBaseEntity` for collection operations - **Interfaces** (`Interfaces/`) — DTO interfaces, soft-delete, foreign key markers - **Models** (`Models/`) — Login request/response -- **Loggers** (`Loggers/`) — `ILogger` abstraction +- **Loggers** (`Loggers/`) — `ILogger` abstraction wrapping AyCode `IAcLoggerBase` ### Mango.Nop.Data — Data Access Layer Wraps nopCommerce data infrastructure with custom base classes: - **`MgDbTableBase`** — extends nopCommerce `EntityRepository`, adds `GetAll()`, timestamp handling (`ITimeStampCreated`, `ITimeStampModified`), event publishing -- **`MgDtoDbTableBase`** — DTO-aware repository with entity ↔ DTO mapping -- **`MgDbContextBase`** — EF Core context base for custom tables -- **`MgDalBase`** — Data Access Layer orchestrator +- **`MgDtoDbTableBase`** — DTO-aware repository. **Critical: Delete operations throw — must use `DeleteMainEntityById()`.** Event bridging: DTO events → main entity events +- **`MgDbContextBase`** — DB context base with `Transaction/TransactionSafe/TransactionAsync/TransactionSafeAsync` methods. TransactionSafe variants use global `SemaphoreSlim` lock +- **`MgDalBase`** — Data Access Layer orchestrator with `Context`, `Name`, `MutextLock` ### Mango.Nop.Services — Service Layer Service base classes for nopCommerce plugin development: -- **`MgBackgroundServiceBase`** — hosted background task base -- **`MgSessionServiceBase`** / `MgSessionItemBase` — session management -- **`MgEventConsumerBase`** — nopCommerce entity event handler base -- **`MgLockServiceBase`** — distributed locking -- **`NopLogWriter`** — logging bridge to nopCommerce log system +- **`MgBackgroundServiceBase`** — hosted background task with configurable interval, pause support, per-iteration error handling +- **`MgSessionServiceBase`** / `MgSessionItemBase` — in-memory session management via `ConcurrentDictionary`, SignalR connection tracking +- **`MgEventConsumerBase`** — nopCommerce entity event handler base (Product insert/update, CustomerRegistered, OrderPlaced, PageRendering, ProductSearch) +- **`MgLockServiceBase`** — `SemaphoreSlim(1)` lock wrapper +- **`NopLogWriter`** — logging bridge: AyCode log levels → nopCommerce `Log` table via direct DB insert with `TransactionScope(Suppress)` ## NopDependencies Pattern @@ -56,6 +56,70 @@ public abstract partial class BaseEntity : IBaseEntity This allows `Mango.Nop.Core` to be referenced by projects that don't have a direct nopCommerce dependency (e.g., Blazor/MAUI clients), while maintaining type compatibility with the real nopCommerce entities at the server level. +**Files in NopDependencies:** +- `BaseEntity.cs` + `IBaseEntity` — root entity base (`Nop.Core`) +- `Catalogs/` — `Customer`, `CustomerRole`, `Order`, `OrderItem`, `OrderNote`, `Product`, `GenericAttribute`, `StockQuantityHistory`, `DiscountMapping`, `DiscountProductMapping` +- Enums — `OrderStatus`, `PaymentStatus`, `ShippingStatus`, `ProductType`, `ManageInventoryMethod`, `BackorderMode`, `LowStockActivity`, `GiftCardType`, `RentalPricePeriod`, `RecurringProductCyclePeriod`, `DownloadActivationType`, `TaxDisplayType`, `VatNumberStatus` +- Interfaces — `ISoftDeletedEntity`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `IDiscountSupported` + +## DTO Mapping Strategies + +Two patterns coexist: + +### Strategy 1: `ModelDtoBase` (simple DTOs) +Used by: `CustomerDto`, `MgGenericAttributeDto` +``` +ModelDtoBase → ModelDtoBase → CustomerDto +``` +- Manual `CopyEntityValuesToDto`/`CopyDtoValuesToEntity` overrides +- No LinqToDB associations + +### Strategy 2: `MgEntityBase` + `IModelDtoBase` (complex DTOs) +Used by: `MgOrderDto`, `MgOrderItemDto`, `MgStockQuantityHistoryDto` +``` +BaseEntity → MgEntityBase → MgOrderDto : IModelDtoBase +``` +- Uses `PropertyHelper.CopyPublicValueTypeProperties()` for bulk copy +- LinqToDB `[Association]` navigation properties +- Generic type parameters for child DTOs + +### Strategy 3: Entity inheritance (MgProductDto) +``` +BaseEntity → MgEntityBase → MgProductDto : IMgProductDto +``` +- No `IModelDtoBase` (entity mapping methods are commented out) +- Direct property declarations mirroring `Product` fields + +## Transaction Pattern + +`MgDbContextBase` provides 4 transaction methods: + +| Method | Lock | Async | +|---|---|---| +| `Transaction(callback)` | No | No | +| `TransactionSafe(callback)` | `SemaphoreSlim` | No | +| `TransactionAsync(callback)` | No | Yes (thread pool) | +| `TransactionSafeAsync(callback)` | `SemaphoreSlim` | Yes (thread pool) | + +**Callback contract:** `Func)>` — return `true` to commit, `false` to rollback. +**Isolation level:** `ReadCommitted` +**Error handling:** Catches exceptions, logs, returns `false` (unless `throwException = true`) + +## Logging Architecture + +Base logging infrastructure (`IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase`, `AcLogItem`) is defined in AyCode.Core — see `AyCode.Core/docs/LOGGING.md` for base types, log levels, and writer contracts. + +This layer provides the nopCommerce-specific bridge: + +``` +Application code → Mango.Nop.Core.Loggers.ILogger (extends IAcLoggerBase) + → Logger (extends AcLoggerBase, delegates to IAcLogWriterBase[]) + → NopLogWriter (in Services, extends AcLogItemWriterBase) → Nop Log table (direct SQL insert) + → [Other AyCode log writers: console, file, SignalR, etc. — see AyCode.Core/docs/LOGGING.md] +``` + +**Important:** `MgBackgroundServiceBase` uses nopCommerce's `Nop.Services.Logging.ILogger` directly (not the Mango wrapper). Other services use `Mango.Nop.Core.Loggers.ILogger`. + ## Reference Modes ``` diff --git a/docs/CONVENTIONS.md b/docs/CONVENTIONS.md index 97b30da..d23381a 100644 --- a/docs/CONVENTIONS.md +++ b/docs/CONVENTIONS.md @@ -7,17 +7,79 @@ - **`Dto` suffix** for DTOs wrapping nopCommerce entities: `MgOrderDto`, `MgProductDto`, `MgOrderItemDto`. - **`DbTable` suffix** for repository classes: `MgDbTableBase`, `MgDtoDbTableBase`. - **`Base` suffix** for abstract base classes: `MgEntityBase`, `ModelDtoBase`, `MgBackgroundServiceBase`. +- **`Nop` prefix** for nopCommerce bridge types: `NopLogWriter`, `NopLoggerMsSqlNopDataProvider`, `NopCommonConst`. ## Patterns -- **DTO bidirectional mapping** — `ModelDtoBase` provides `CopyEntityValuesToDto(entity)` and `CreateMainEntity()`. Override both in concrete DTOs. -- **NopDependencies mirror** — entity classes in `NopDependencies/` use the **same namespace** as the original nopCommerce types. Do not change namespaces. -- **GenericAttribute typed access** — use `GenericAttributeExtensions.GetValueOrDefault()` / `GetValueOrNull()` instead of raw string parsing. -- **Repository base chain** — `MgDbTableBase` → `EntityRepository` (nopCommerce). Override virtual methods, don't replace the chain. -- **Timestamp interfaces** — entities implementing `ITimeStampCreated` / `ITimeStampModified` get automatic timestamp management in `MgDbTableBase`. +### DTO Bidirectional Mapping +- `ModelDtoBase` provides `CopyEntityValuesToDto(entity)`, `CopyDtoValuesToEntity(entity)`, and `CreateMainEntity()`. Override all three in concrete DTOs. +- Simple DTOs (e.g. `CustomerDto`) — manual property-by-property copy in overrides. +- Complex DTOs (e.g. `MgOrderDto`) — use `PropertyHelper.CopyPublicValueTypeProperties(source, target)` for automatic value-type property copy, then manually set reference/navigation properties. + +### LinqToDB Associations +- DTOs with navigation properties use `[Association(ThisKey, OtherKey, CanBeNull)]` from LinqToDB. +- `ThisKey` = local FK property name, `OtherKey` = target entity property name (typically `Id` or via interface member like `nameof(IMgProductDto.Id)`). + +### NopDependencies Mirror +- Entity classes in `NopDependencies/` use the **same namespace** as the original nopCommerce types. Do not change namespaces. +- All mirror classes are `partial` — they can be extended but should not be modified directly. + +### GenericAttribute Typed Access +- Use `GenericAttributeExtensions.GetValueOrDefault()` / `GetValueOrNull()` / `TryGetValue()` instead of raw string parsing. +- Use `AddNewGenericAttribute()` to create new attributes with automatic UTC timestamp. + +### Repository Base Chain +- `MgDbTableBase` → `EntityRepository` (nopCommerce). Override virtual `OnInsert`/`OnUpdate`/`OnDelete` hooks, don't replace the chain. +- `MgDtoDbTableBase` → `MgDbTableBase`. **Never call Delete directly** — always use `DeleteMainEntityById(int id)`. + +### Timestamp Interfaces +- Entities implementing `ITimeStampCreated` get `Created = DateTime.UtcNow` on insert. +- Entities implementing `ITimeStampModified` get `Modified = DateTime.UtcNow` on insert and update. +- `ITimeStampInfo` combines both (`Creator`, `Created`, `Modified`). + +### Transaction Pattern +- Use `MgDbContextBase.TransactionSafeAsync()` for operations needing global lock (order creation, stock adjustment). +- Callback returns `bool` — `true` commits, `false` rolls back. +- Exceptions are caught and logged by default (return `false`). Pass `throwException: true` only for fail-fast scenarios. + +### Event Consumer Pattern +- Inherit `MgEventConsumerBase` and override relevant `HandleEventAsync` methods. +- Base class handles DI of `IMgDbContextBase`, `IHttpContextAccessor`, and `IAcLogWriterBase[]`. +- Base provides `CheckAndUpdateProductManageInventoryMethodToManageStock(Product)` helper. + +### Session Pattern +- Inherit `MgSessionServiceBase` and `MgSessionItemBase`. +- Sessions stored in `ConcurrentDictionary` — thread-safe, in-memory only. +- Session items track `SessionId`, `SignaRConnectionId`, `RequestCount`. + +### Logging +- Base logging infrastructure (`IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase`) — see `AyCode.Core/docs/LOGGING.md`. +- Services create loggers via `new Logger(logWriters.ToArray())` in constructor. +- `logWriters` injected as `IEnumerable` — typically contains `NopLogWriter` + other writers. +- **Exception:** `MgBackgroundServiceBase` uses `Nop.Services.Logging.ILogger` directly (nopCommerce logger), not the Mango wrapper. + +### Serialization Attributes +- `[ToonDescription(...)]` — AyCode metadata for AI/doc tooling. `Purpose`, `BusinessRule`, `TypeRelation`, `RelatedTypes` properties. +- `[AcBinarySerializable(false, true, false, true)]` — AcBinarySerializer config (see `AyCode.Core/docs/BINARY_FORMAT.md`). Parameters control serialization behavior for AcSignalR transport (see `AyCode.Core/AyCode.Services/docs/SIGNALR.md`). +- LinqToDB `[Table(Name = "...")]` and `[Association(...)]` — DB mapping. ## Project Boundaries -- `Mango.Nop.Core` — NO nopCommerce runtime dependency. Only NopDependencies mirrors. -- `Mango.Nop.Data` — depends on nopCommerce data layer (`Nop.Core`, `Nop.Data`). +- `Mango.Nop.Core` — NO nopCommerce runtime dependency. Only NopDependencies mirrors. No `Nop.Data`, `Nop.Services` references. +- `Mango.Nop.Data` — depends on nopCommerce data layer (`Nop.Core`, `Nop.Data`). No `Nop.Services` reference. - `Mango.Nop.Services` — depends on full nopCommerce stack (includes `Nop.Services`, `Nop.Web.Framework`). + +## AyCode Integration Points + +Key AyCode types used across these libraries: +- `IEntityInt` (AyCode.Interfaces.Entities) — `int Id` entity contract +- `IBaseEntity` — defined locally in NopDependencies, mirrors AyCode's concept +- `IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase` — logging infrastructure (see `AyCode.Core/docs/LOGGING.md`) +- `IAcModelDtoBaseEmpty` — DTO marker interface +- `IAcSoftRemoveEntity`, `IAcSoftRemoveEntityInt` — soft-delete contracts +- `IForeignKey`, `IForeignCollection` — FK marker interfaces +- `ITimeStampCreated`, `ITimeStampModified`, `ITimeStampInfo` — timestamp contracts +- `PropertyHelper.CopyPublicValueTypeProperties()` — reflection-based property copy +- `TaskHelper.ToThreadPoolTask()` — wraps async work in thread pool +- `AcConst` — abstract constants base +- `ToonDescription` — metadata attribute for AI tooling diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md index f043af6..7d71ed9 100644 --- a/docs/GLOSSARY.md +++ b/docs/GLOSSARY.md @@ -6,27 +6,58 @@ Terminology for the Mango.Nop Libraries. Read this before working on unfamiliar | Term | Definition | |---|---| -| **BaseEntity** | nopCommerce root entity base with `int Id`. Mirror copy in `NopDependencies/`. | -| **MgEntityBase** | Custom entity base — inherits `BaseEntity`, implements `IEntityInt` (AyCode). Used for domain-specific entities (e.g., `MgStockTaking`). | -| **ModelDtoBase\** | Abstract DTO base with bidirectional mapping: `CopyEntityValuesToDto()` reads from entity, `CreateMainEntity()` creates entity from DTO. | +| **BaseEntity** | nopCommerce root entity base with `int Id`. Mirror copy in `NopDependencies/`. Namespace: `Nop.Core`. | +| **IBaseEntity** | Interface for `BaseEntity` (defined alongside it in NopDependencies). Only `int Id`. | +| **MgEntityBase** | Custom entity base — inherits `BaseEntity`, implements `IEntityInt` (AyCode). `ToString()` → `"{TypeName}; Id: {Id}"`. | +| **ModelDtoBase** | Non-generic DTO base — only `int Id`. Implements `IModelDtoBase`. | +| **ModelDtoBase\** | Generic DTO base with bidirectional mapping: `CopyEntityValuesToDto()` reads from entity, `CopyDtoValuesToEntity()` writes to entity, `CreateMainEntity()` creates entity from DTO. Uses `Activator.CreateInstance()`. | +| **IModelDtoBase** | `IEntityInt` + `IModelDtoBaseEmpty`. Marker for all DTOs with `int Id`. | +| **IModelDtoBase\** | Contract: `CreateMainEntity()`, `CopyDtoValuesToEntity()`, `CopyEntityValuesToDto()`. | | **NopDependencies** | Mirror copies of nopCommerce entity classes in `Mango.Nop.Core`. Same namespaces as originals. Eliminates full nopCommerce dependency for consumers that only need types. | -| **GenericAttribute** | nopCommerce polymorphic key-value store. `KeyGroup` = owner type name, `EntityId` = owner ID, `Key` = attribute name, `Value` = string value. | +| **GenericAttribute** | nopCommerce polymorphic key-value store. `KeyGroup` = owner type name, `EntityId` = owner ID, `Key` = attribute name, `Value` = string value. `CreatedOrUpdatedDateUTC` tracks last change. | +| **PropertyHelper.CopyPublicValueTypeProperties** | AyCode utility — reflection-based copy of all public value-type properties between two objects. Used by complex DTOs (`MgOrderDto`, `MgOrderItemDto`). | +| **ToonDescription** | AyCode metadata attribute for AI/doc tooling. Properties: `Purpose`, `BusinessRule`, `TypeRelation`, `RelatedTypes`. | +| **AcBinarySerializable** | AyCode binary serialization attribute (AcBinarySerializer, see `AyCode.Core/docs/BINARY_FORMAT.md`). Configures how a type is serialized for AcSignalR transport (see `AyCode.Core/AyCode.Services/docs/SIGNALR.md`). | ## Data Access | Term | Definition | |---|---| -| **MgDbTableBase\** | Repository base wrapping nopCommerce `EntityRepository`. Adds `GetAll()`, automatic timestamp management, event publishing. | -| **MgDtoDbTableBase** | DTO-aware repository — handles DTO ↔ entity mapping on top of `MgDbTableBase`. | -| **MgDbContextBase** | EF Core `DbContext` base for custom (non-nopCommerce) tables. | -| **MgDalBase** | Data Access Layer orchestrator — coordinates multiple repositories. | +| **MgDbTableBase\** | Repository base wrapping nopCommerce `EntityRepository`. Adds `GetAll()` (`IQueryable`), automatic timestamp management via `ITimeStampCreated`/`ITimeStampModified` interfaces, CRUD hooks (`OnInsert`, `OnUpdate`, `OnDelete`). | +| **MgDtoDbTableBase\** | DTO-aware repository. **Delete operations throw** — must use `DeleteMainEntityById()`. Bridges DTO events → main entity events (`EntityInsertedEvent`, `EntityUpdatedEvent`). | +| **MgDbContextBase** | Database context base. Exposes `IRepository`, `IRepository`, `IRepository`, `INopDataProvider`. Provides 4 transaction methods (`Transaction`, `TransactionSafe`, `TransactionAsync`, `TransactionSafeAsync`). | +| **MgDalBase\** | Data Access Layer orchestrator — `Name`, `Context` (typed `TDbContext`), `MutextLock` (cross-process `Mutex`). | +| **TransactionSafe** | Transaction with global `SemaphoreSlim` lock. Prevents concurrent modifications. Use for order creation, stock changes. | +| **EntityRepository\** | nopCommerce built-in repository base (`Nop.Data`). `MgDbTableBase` extends this. | +| **INopDataProvider** | nopCommerce interface for raw LinqToDB data access. Used by `MgDbContextBase` and `MgDtoDbTableBase`. | ## Services | Term | Definition | |---|---| -| **MgBackgroundServiceBase** | Base for hosted background services within nopCommerce. | -| **MgSessionServiceBase** | Session state management — tracks user sessions. | -| **MgEventConsumerBase** | Base for nopCommerce entity event consumers (insert/update/delete notifications). | -| **MgLockServiceBase** | Distributed lock service — prevents concurrent operations on shared resources. | -| **NopLogWriter** | Logging bridge — writes to nopCommerce's built-in log system. | +| **MgBackgroundServiceBase** | Base for hosted background services. Loop: `Task.Delay(interval)` → `OnExecuteAsync()`. Supports pause. Per-iteration exception handling. Uses nopCommerce `ILogger`. | +| **MgSessionServiceBase\** | In-memory session management via `ConcurrentDictionary`. Thread-safe. Methods: `GetOrCreateSessionItem`, `TryAddSessionItem`, `TryGetSessionItem`, `TryRemoveSessionItem`, `TryGetSessionItemBySignlaRConnectionId`. | +| **MgSessionItemBase** | Session item with `SessionId`, `SignaRConnectionId`, `RequestCount`. Primary constructor: `(string sessionKey)`. | +| **MgEventConsumerBase** | nopCommerce event consumer base. Subscribes to: `EntityUpdatedEvent`, `EntityInsertedEvent`, `CustomerRegisteredEvent`, `OrderPlacedEvent`, `PageRenderingEvent`, `ProductSearchEvent`. All handlers virtual with empty default. | +| **MgLockServiceBase** | `SemaphoreSlim(1)` wrapper. Implements `IMgLockService`. Used by `MgDbContextBase.TransactionSafe*`. | +| **NopLogWriter** | Bridges AyCode logging (see `AyCode.Core/docs/LOGGING.md`) to nopCommerce `Log` table. +| **NopLoggerMsSqlNopDataProvider** | Extends `MsSqlNopDataProvider`. Provides `InsertLogItem()` / `InsertLogItemAsync()` with own `TransactionScope(Suppress, ReadUncommitted)` to avoid transaction nesting. | + +## AyCode Types (external, from DLL references) + +| Term | Definition | +|---|---| +| **IEntityInt** | `int Id` entity contract (AyCode.Interfaces.Entities) | +| **IAcLoggerBase** | Logger interface — see `AyCode.Core/docs/LOGGING.md` (`AyCode.Core.Loggers`) | +| **IAcLogWriterBase** | Log writer interface — pluggable output sink (see `AyCode.Core/docs/LOGGING.md`) | +| **AcLoggerBase** | Logger base class with category name and writer array (see `AyCode.Core/docs/LOGGING.md`) | +| **AcLogItemWriterBase\** | Log writer base for structured log items (see `AyCode.Core/docs/LOGGING.md`) | +| **IAcModelDtoBaseEmpty** | Empty DTO marker interface | +| **IAcSoftRemoveEntity** | Soft-delete contract (AyCode) | +| **IForeignKey** | FK marker interface | +| **IForeignCollection\** | FK collection marker | +| **ITimeStampCreated** | `Created` datetime property | +| **ITimeStampModified** | `Modified` datetime property | +| **ITimeStampInfo** | Combines `Creator` (int), `Created`, `Modified` | +| **AcConst** | Abstract constants base class | +| **TaskHelper.ToThreadPoolTask** | Wraps `Func>` into `Task.Run` on thread pool |