FruitBankHybridApp/docs/ARCHITECTURE.md

6.6 KiB
Raw Blame History

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.0android/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 (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 databaseDatabaseClient caches entities in ConcurrentDictionary for offline/fast access
  • Platform-specific credential storage — MAUI uses SecureStorage, Web uses obfuscated localStorage, Server uses no-op