7.6 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 (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.mdanddocs/- 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
Project-level adapter that fixes 3 of 4 generic parameters:
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:
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