6.7 KiB
6.7 KiB
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:
../../../Aycode/Source/AyCode.Core/— SignalR, serialization, binary protocol, data sources- AyCode.Blazor:
../../../Aycode/Source/AyCode.Blazor/— MgGridBase, UI components, layout persistence- Mango.Nop Libraries:
../NopCommerce.Common/4.70/Libraries/— independent library with own copilot-instructions 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.
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/docs/MGGRID.md 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
Project-level adapter that fixes 3 of 4 generic parameters:
public class FruitBankGridBase<TDataItem>
: MgGridBase<SignalRDataSourceObservable<TDataItem>, TDataItem, int, LoggerClient>
Adds FruitBank-specific defaults:
GetLayoutUserId()→LoggedInModel.CustomerDto?.Id ?? 0(per-user layout)- Master grid: filter row, group panel, search box,
PageSize=20,EditMode=EditRow - Detail grid: no filter/group/search,
PageSize=10 - Alternating row style (
.alt-item), header background (#E6E6E6) - Detail rows hidden for non-admin users (
hideDetailButtonCSS)
SignalR Tag Mapping
Each concrete grid sets CRUD tags in its constructor:
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)
<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 —
DatabaseClientcaches entities in ConcurrentDictionary for offline/fast access - Platform-specific credential storage — MAUI uses SecureStorage, Web uses obfuscated localStorage, Server uses no-op