# Architecture

## Framework vs. Consumer Boundary

Layer 0 (Core framework). Consumers (plural, unknown) reference downward only.

### Layer hierarchy

```
Layer 0 — Core framework           this solution (AyCode.Core)
Layer 1 — UI framework             e.g. AyCode.Blazor — Blazor/MAUI bases
Layer 2 — Domain framework         e.g. Mango.Nop.Core — NopCommerce-plugin bases
Layer 3 — Consumer application     the actual business app
```

**Dependencies flow downward only** — framework never references consumer.

### What belongs here vs. in a consumer

**Yes, framework:**
- Abstract base classes with hooks for consumer override
- Interfaces and contracts
- Options classes for consumer configuration
- Generic logic parameterized by consumer types

**No, consumer only:**
- Business logic
- Consumer-named types or namespaces
- Hardcoded URLs, tenants, or product IDs

### Minimum-boilerplate ideal

Aim for minimal consumer setup:

```csharp
// Consumer Program.cs — ideal pattern
services.Configure<AcXxxOptions>(config.GetSection("AyCode:Xxx"));
services.AddAcXxxFactory<MyConcreteType>();
```

Verbose consumer code → incomplete framework. Promote recurring patterns to extension methods, base classes, or options.

### Promotion pattern

When a pattern appears in 2+ consumer projects:
1. Identify generic vs. consumer-specific parts
2. Move generic part → appropriate framework layer (abstract base, options, or extension)
3. Leave specific part in consumer (override or configure)

Pattern: **"write the base first, derive the specific later"** — plan the framework abstraction before consumer-specific code.

### Class prefix — framework-only mandate

**Workspace-wide convention** — every framework-typed repo (`@repo.type = "framework"` in `.github/copilot-instructions.md`) MUST prefix its public types with a stable repo-family prefix:

| Family        | Prefix | Example types                                       |
|---------------|--------|-----------------------------------------------------|
| AyCode.*      | `Ac`   | `AcDalBase`, `AcBinarySerializer`, `IAcUserDbSet`   |
| Mango.Nop.*   | `Mg`   | `MgEntityBase`, `MgDbTableBase`, `MgOrderDto`       |

**Product/Consumer repos** (`@repo.type = "product"` or `"consumer"`) **MUST NOT** prefix their domain types. Product types are concrete derivations of framework abstractions and use natural domain names (e.g. `Order`, `ShippingItem`, `OrderItemPallet` — no product-specific prefix).

**Why:** prefix marks code as framework-bequeathed. `Order` (un-prefixed) = product concrete; `AcDalBase` = framework abstraction. Cross-repo patterns self-document: `AcDalBase<Order>` reads as "framework abstraction over product concrete" without lookup.

## Dependency Graph

```
AyCode.Utils (zero dependencies)
    ↑
AyCode.Interfaces → AyCode.Entities → AyCode.Models
    ↑                    ↑                  ↑
AyCode.Core ─────────────┘                  │
    ↑                                       │
AyCode.Database ────────────────────────────┘
    ↑
AyCode.Services ← AyCode.Services.Server
```

**Rule:** Dependencies flow upward only — lower layers never reference higher.

## Project Roles

### Foundation Layer
- **AyCode.Utils** — Zero-dependency utilities. String/DateTime extensions, lock wrappers.
- **AyCode.Interfaces** — Pure interfaces. `IId<T>` is the root abstraction.
- **AyCode.Entities** — Abstract generic entity classes. Never instantiated directly.
- **AyCode.Models** — DTOs and view models for service boundaries.

### Core Layer
- **AyCode.Core** — Serializers (Binary, JSON, Toon), compression (Brotli, GZip, LZ4), logging framework, constants, validation.
- **AyCode.Core.Serializers.SourceGenerator** — Roslyn incremental generator. Targets netstandard2.0. Generates `IGeneratedBinaryWriter` / `IGeneratedBinaryReader` for `[AcBinarySerializable]` types.

### Data Layer
- **AyCode.Database** — EF Core with generic DAL pattern. Session for reads, Transaction for writes. DAL pooling via `PooledDal`.

### Service Layer
- **AyCode.Services** — Client-side: SignalR client, login service, loggers, ASP.NET Core MVC formatters for AcBinary (`Mvc/` — **temporarily disabled**, see `AyCode.Services/Mvc/README.md`).
- **AyCode.Services.Server** — Server-side: SignalR hub with custom binary protocol, email (SendGrid), JWT auth.
- **AyCode.Models.Server/DynamicMethods** — Reflection-based tag→method dispatch used by the SignalR hub.

> **SignalR Dispatch:** Both directions use a single method `OnReceiveMessage(int messageTag, int? requestId, SignalParams signalParams, object data)` with integer tag-based routing instead of standard Hub methods. Write path: zero-copy via `AcBinarySerializer.Serialize(value, output)` directly to pipe. Read path: protocol eagerly deserializes `data` to typed object via `SignalParams.SignalDataType`, or returns raw `byte[]` for `IsRawBytesData`/byte[] fast-path. See `AyCode.Services/docs/SIGNALR/README.md` for full details.

### Server Extensions
- **AyCode.Core.Server**, **AyCode.Interfaces.Server**, **AyCode.Entities.Server**, **AyCode.Models.Server** — Server-only additions that don't belong in shared code.

## Project Layout — Shared/Server Split

**Workspace-wide convention.** Each logical project gets one or two physical projects, named by suffix:

| Suffix       | Contains                                                          | Visibility   |
|--------------|-------------------------------------------------------------------|--------------|
| `Foo`        | Code shared by client and server (DTOs, interfaces, common logic) | Both         |
| `Foo.Server` | Server-only code (data access, hosting, server-side services)     | Server only  |
| `Foo.Client` | RARE — only when truly client-only code exists                    | Client only  |

**Examples already in this repo:** `AyCode.Core` + `AyCode.Core.Server`, `AyCode.Interfaces` + `AyCode.Interfaces.Server`, `AyCode.Entities` + `AyCode.Entities.Server`, `AyCode.Models` + `AyCode.Models.Server`, `AyCode.Services` + `AyCode.Services.Server`. **No `.Client` projects exist by default.**

### Why no symmetric `.Client`

- Most client-relevant code is genuinely shared (DTOs, common logic).
- Doubling every project to `.Client` + `.Server` would balloon the workspace without proportional benefit.
- Create `.Client` only for true client-only code (rare).

### Why no security risk

- The boundary is **directional**: server may reference shared; client must not reference `.Server`.
- Enforcement at the **project-graph + DLL reference level**, not the suffix — a client referencing `.Server` fails to build.
- Shared code is by design safe for both sides.

### When a new project is added

Default: **one shared `Foo` project**. Add `Foo.Server` when server-only code accumulates. Add `Foo.Client` only for the rare case above.

## Serialization Architecture

Three serializers share a common infrastructure but serve different goals:

| Serializer | Primary Goal | Use Case |
|---|---|---|
| **AcBinary** | Speed | Wire protocol, SignalR, storage |
| **AcJson** | Compatibility | REST APIs, debugging, interop |
| **Toon** | LLM Accuracy | AI context, schema documentation |

## Generic Entity Pattern

Entities use composition via generic type parameters:

```csharp
// Interface layer
interface IAcUser<TProfile, TCompany> : IId<Guid> { ... }

// Entity layer (abstract)
abstract class AcUser<TProfile, TCompany, TUserToCompany, TAddress> { ... }

// Consuming project (concrete)
class User : AcUser<Profile, Company, UserToCompany, Address> { ... }
```

Framework defines relationships without knowing concrete types.
