# Architecture

## Solution Dependency Graph

```
FruitBank.Common (shared domain, net9.0)
    ↑
FruitBank.Common.Server (server-side services, nopCommerce, net9.0)
    ↑
FruitBankHybrid.Shared.Common (shared utilities, net10.0)
    ↑
FruitBankHybrid.Shared (Blazor UI components, pages, services, net10.0)
    ↑                ↑                      ↑
FruitBankHybrid    FruitBankHybrid.Web    FruitBankHybrid.Web.Client
(MAUI, net10.0)    (Server, net10.0)      (WASM, net10.0)
```

All projects also reference **AyCode.Core** (net9.0) and **AyCode.Blazor** (net10.0) via DLL references (not ProjectReference).

> **Context:** When a type or base class is not found in this solution, browse the external repos:
> - **AyCode.Core** (in AyCode.Core repo) — SignalR, serialization, binary protocol, data sources
> - **AyCode.Blazor** (in AyCode.Blazor repo) — MgGridBase, UI components, layout persistence
> - **Mango.Nop Libraries**: `../NopCommerce.Common/4.70/Libraries/` — independent library with own `.github/copilot-instructions.md` and `docs/`
> - **FruitBank Plugin**: `../NopCommerce.Common/4.70/Plugins/Nop.Plugin.Misc.AIPlugin/` — server-side SignalR hubs, measurement services, DbTable classes

## Target Frameworks

nopCommerce 4.80.9 requires .NET 9

| Project | TFM | Reason |
|---|---|---|
| FruitBank.Common | net9.0 | nopCommerce entity references |
| FruitBank.Common.Server | net9.0 | nopCommerce server integration |
| FruitBankHybrid.Shared | net10.0 | Blazor UI |
| FruitBankHybrid.Shared.Common | net10.0 | Shared utilities |
| FruitBankHybrid | net10.0‑android/ios/win | MAUI Hybrid |
| FruitBankHybrid.Web | net10.0 | Blazor Server host |
| FruitBankHybrid.Web.Client | net10.0 | Blazor WASM |
| FruitBankHybrid.Shared.Tests | net10.0 | Tests |
| **AyCode.Core** (external) | net9.0 | Foundation — used by nopCommerce layer |
| **AyCode.Blazor** (external) | net10.0 | UI framework |
| **AyCode.Core.Serializers.SourceGenerator** | netstandard2.0 | Roslyn analyzer requirement |

## Three Deployment Targets

| Target | Project | How UI Runs |
|---|---|---|
| **MAUI Hybrid** | FruitBankHybrid | Native app with BlazorWebView |
| **Blazor Server** | FruitBankHybrid.Web | Server-side rendering + SignalR |
| **Blazor WASM** | FruitBankHybrid.Web.Client | Downloaded to browser, runs in WASM |

All three share the same UI components from `FruitBankHybrid.Shared`.

## Shared Configuration

`FruitBankHybrid.Shared/appsettings.json` is the **canonical configuration source** for all three hosts. Edits go ONLY into the Shared file — never edit the host-side copies (they are build artifacts). Each host pulls it from disk via a different mechanism; the Razor SDK auto-publish behavior is suppressed in `FruitBankHybrid.Shared.csproj` to avoid NETSDK1152 ("multiple publish output files with the same relative path") collisions when the Shared file would otherwise also flow into each host's publish output through the project reference.

| Host | How it pulls the config | Resulting path |
|---|---|---|
| `FruitBankHybrid.Web` | `<Target Copy>` (`BeforeBuild`) | `appsettings.json` (project root, ASP.NET Core ContentRoot) |
| `FruitBankHybrid.Web.Client` | `<Target Copy>` (multiple early `BeforeTargets`) | `wwwroot/appsettings.json` (static web asset) |
| `FruitBankHybrid` (MAUI) | `<EmbeddedResource Link="appsettings.json" LogicalName="FruitBankHybrid.appsettings.json" />` | manifest resource — loaded via `GetManifestResourceStream` in `MauiProgram.cs` |

A clean build (delete `obj/`) is required the first time on Web.Client because the static-asset manifest is cached in `obj/`.

## Data Flow

```
User → DevExpress Grid → AcSignalRDataSource → SignalR (AcBinary) → DevAdminSignalRHub
                                                                          ↓
                                                                    DynamicMethodRegistry
                                                                          ↓
                                                          IFruitBankDataControllerServer
                                                          ICustomOrderSignalREndpointServer
                                                          IStockSignalREndpointServer
                                                                          ↓
                                                                  nopCommerce Database
```

## MgGrid — Grid System

All data screens use **MgGridBase** from AyCode.Blazor (see `AyCode.Blazor.Components/docs/MGGRID/README.md` (in AyCode.Blazor repo) for full technical docs).

### FruitBank Grid Hierarchy

```
MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient>   (AyCode.Blazor)
  └── FruitBankGridBase<TDataItem>                                (FruitBankHybrid.Shared)
        ├── GridShippingBase          → Shipping
        ├── GridShippingDocumentBase  → ShippingDocument (detail of Shipping)
        ├── GridPartnerBase           → Partner
        ├── GridGenericAttributeBase  → GenericAttributeDto
        └── GridStockTakingItemBase   → StockTakingItem
```

### FruitBankGridBase<TDataItem>

Project-level adapter that fixes 3 of 4 generic parameters:

```csharp
public class FruitBankGridBase<TDataItem>
    : MgGridBase<SignalRDataSourceObservable<TDataItem>, TDataItem, int, LoggerClient>
```

Adds FruitBank-specific defaults (master vs detail grid settings, alternating row style, header background). For the full settings table see `FruitBankHybrid.Shared/Components/Grids/README.md`.

### SignalR Tag Mapping

Each concrete grid sets CRUD tags in its constructor:

```csharp
public GridShippingBase()
{
    GetAllMessageTag = SignalRTags.GetShippings;   // e.g. 300
    AddMessageTag    = SignalRTags.AddShipping;     // e.g. 302
    UpdateMessageTag = SignalRTags.UpdateShipping;  // e.g. 303
}
```

Tags are defined in `FruitBank.Common/SignalRs/SignalRTags.cs` with numeric ranges per domain area.

### Grid Usage Pattern (Razor)

```razor
<MgGridWithInfoPanel ShowInfoPanel="@IsMasterGrid">
    <GridContent>
        <GridShippingBase @ref="Grid"
                          DataSource="Shippings"
                          AutoSaveLayoutName="GridShipping"
                          SignalRClient="FruitBankSignalRClient"
                          Logger="_logger"
                          OnGridFocusedRowChanged="Grid_FocusedRowChanged">
            <Columns>
                <DxGridDataColumn FieldName="Id" SortIndex="0" SortOrder="Descending" ReadOnly />
                <DxGridDataColumn FieldName="ShippingDate" Caption="Beérkezés" />
                ...
            </Columns>
            <DetailRowTemplate>
                @{ var shipping = (Shipping)context.DataItem; }
                <GridShippingDocument ParentDataItem="@shipping" ... />
            </DetailRowTemplate>
        </GridShippingBase>
    </GridContent>
</MgGridWithInfoPanel>
```

## Key Architectural Decisions

- **nopCommerce plugin** — Customer, Order, Product are nopCommerce entities extended via GenericAttributes and DTOs
- **SignalR over REST** — all data flows through SignalR with AcBinary protocol
- **DevExpress Blazor 25.1.3** — exclusive UI component library
- **MgGridBase** — canonical grid base from AyCode.Blazor for all data screens (SignalR CRUD, layout persistence, master-detail)
- **Three measurement hierarchies** — Shipping/Order/StockTaking share same base but have different audit rules
- **Client-side database** — `DatabaseClient` caches entities in ConcurrentDictionary for offline/fast access
- **Platform-specific credential storage** — MAUI uses SecureStorage, Web uses obfuscated localStorage, Server uses no-op
