Skip to content

πŸ—οΈ ConnectSoft Blazor Templates β€” High-Level Design

The ConnectSoft Blazor Templates provide a standardized, enterprise-ready foundation for building SaaS portals. They unify component reuse, microfrontend modularity, and portal orchestration into one cohesive blueprint, ensuring faster delivery and consistent user experiences across all products.


🧱 What We Deliver

  1. Component Library Template

    • Reusable, token-driven Blazor components (buttons, forms, modals).
    • UI-kit agnostic, accessible, and packaged for reuse.
  2. Microfrontend Library Template

    • Independent feature modules, deployable as Web Components.
    • Secure, manifest-driven loading with built-in observability.
  3. Application Shell Template

    • Portal-grade Blazor Web App with navigation, blades, and theming.
    • Tenant/edition policies and secure API access via BFF proxy.

πŸ” Core Guarantees

  • Security & Compliance: Strong defaults (CSP, SRI, signed packages, SBOM).
  • Observability: End-to-end telemetry and monitoring from client to backend.
  • Testability: Automated QA anchors (bUnit, Playwright, coverage gates).
  • Developer Experience: Docs, quickstarts, onboarding guides, and galleries.
  • Distribution: Automated pipelines for NuGet, CDN, and documentation.

πŸ“Š Benefits

  • πŸš€ Speed: Faster onboarding and delivery, fewer reinventions.
  • 🎨 Consistency: Unified UX patterns across portals.
  • πŸ›‘οΈ Trust: Compliance-ready outputs for enterprise needs.
  • πŸ“ˆ Visibility: Observability and diagnostics as defaults.
  • 🌍 Global Reach: Multilingual and RTL support built in.

flowchart LR
  subgraph DesignSystem["🎨 Component Library (RCL)"]
    Tokens["Design Tokens\n+ Tailwind Preset"]
    Components["Reusable Components\n(Button, Modal, Form, ...)"]
    Adapters["UI Kit Adapters\n(Flowbite/DaisyUI/Custom)"]
    Tokens --> Components
    Components --> Adapters
  end

  subgraph MFEs["🧱 Microfrontends (Blazor WASM β†’ Web Components)"]
    MFE1["Billing MFE\n<cs-mfe-billing>"]
    MFE2["Catalog MFE\n<cs-mfe-catalog>"]
    MFE3["Pipelines MFE\n<cs-mfe-pipelines>"]
  end

  subgraph Shell["πŸ›οΈ Application Shell (Blazor Web App)"]
    Nav["Left Rail & Top Bar"]
    Blades["Blade/Portal Layout"]
    Host["MFE Host & Registry\n(SRI, Capabilities, Policy)"]
    Theme["Theme/Density/Culture State"]
    Nav --> Blades --> Host
    Theme --> Host
  end

  subgraph Runtime["βš™οΈ Runtime Services"]
    BFF["BFF / YARP Proxy\nAuth, RBAC, Tenancy"]
    Flags["Feature Flags & Editions\n(ECS/AppConfig)"]
    Realtime["SignalR Notifications & Activity"]
    OTel["Observability\n(OTel Traces/Logs/Metrics)"]
    Sec["Security & Compliance\n(CSP, SRI, SBOM, Signing)"]
  end

  DesignSystem --> MFEs
  MFEs --> Shell
  Shell --> Runtime
  MFEs --> Runtime
Hold "Alt" / "Option" to enable pan & zoom

How to read it:

  • Design System (tokens β†’ components β†’ adapters) ensures UI consistency without UI-kit lock-in.
  • Microfrontends ship as Web Components and can be versioned and deployed independently.
  • The Application Shell composes MFEs at runtime, manages navigation and blades, and governs theme/density/culture.
  • Runtime Services (BFF, flags/editions, SignalR, Observability, Security) provide the enterprise spine: secure APIs, real-time UX, telemetry, and compliance.

πŸ“˜ Vision & Problem Context

🎯 Purpose

The ConnectSoft Blazor Templates provide a standardized, SaaS-ready foundation for frontend development inside the Factory. They unify component reuse, microfrontend modularity, and portal orchestration into a single blueprint family, ensuring every project shares the same UX principles, observability anchors, and integration points.

πŸ’‘ Think of it as the β€œfrontend skeleton key” β€” one set of templates that unlock consistent experiences across products, tenants, and editions.


🌍 Problem Context

Current frontend efforts in SaaS and portal environments face recurring issues:

  • ⚠️ Fragmentation β€” teams create ad-hoc component libraries, each with different patterns.
  • ⚠️ UI Kit Lock-in β€” components are often hard-wired to Flowbite, Telerik, or custom CSS.
  • ⚠️ Duplication β€” buttons, forms, and layouts are rebuilt for every project.
  • ⚠️ Runtime Rigidity β€” MFEs are difficult to load dynamically into a shared shell.
  • ⚠️ Inconsistent UX β€” portals lack unified navigation, theming, and accessibility support.
  • ⚠️ Observability Gaps β€” frontend telemetry is rarely consistent across solutions.

The result: higher cost, slower delivery, and broken coherence across tenant experiences.


🎯 Goals

The templates solve these issues by introducing a unified factory-standard approach:

  • Reusable Component Library Tailwind-driven Razor Class Library, agnostic of UI kits, packaged as NuGet.
  • Pluggable UI Kit Adapters Flowbite, DaisyUI, or other kits are chosen at the shell/application layer, not inside components.
  • Microfrontend Independence Blazor WASM packaged as Web Components, deployed via CDN with manifest metadata.
  • Portal-Ready Application Shell Blazor Web App orchestrating authentication, theming, layout, feature flags, and runtime composition.
  • SaaS Essentials by Default Tenant/edition awareness, OpenTelemetry observability, SignalR live updates, secure BFF/YARP API access.

πŸ“Œ The promise: faster delivery, fewer mismatches, and a consistent SaaS UX no matter which team builds the feature.


🧱 Alignment with the Factory

The templates sit in the Frontend Cluster of the ConnectSoft AI Software Factory:

flowchart TD
  CompLib["🎨 Component Library (RCL)"]
  MFE["🧱 Microfrontend Library (WASM CE)"]
  Shell["πŸ›οΈ Application Shell (Blazor Web App)"]

  CompLib --> MFE --> Shell

  Shell --> ECS["βš™οΈ External Configuration System"]
  Shell --> Services["πŸ“¦ Backend Microservices"]
  Shell --> Agents["πŸ€– Factory Agents"]
Hold "Alt" / "Option" to enable pan & zoom
  • Component Library β†’ standard design tokens and UI blocks.
  • Microfrontends β†’ domain-scoped features deployable independently.
  • Application Shell β†’ the β€œportal spine” hosting MFEs and enforcing tenant rules.

βœ… Guarantees

  • Every frontend artifact is traceable to this vision block.
  • All downstream templates are idempotent: regeneration won’t diverge unless vision changes.
  • UX, observability, and SaaS alignment are non-negotiable defaults.

πŸ“ Frontend Architectural Principles

🎯 Purpose

Establish the guiding standards and design principles that govern all ConnectSoft Blazor templates. These principles ensure that components, microfrontends, and application shells are built with consistency, scalability, and SaaS readiness across every project in the Factory.

πŸ’‘ Principles are the guardrails β€” they prevent drift, enforce quality, and make every template dependable out of the box.


πŸ“ Core Principles

  • Clean Separation of Concerns UI rendering, state management, and service integration layers must remain isolated.

  • Cloud-Native by Default Templates are optimized for SaaS portals, multi-tenancy, edition management, and distributed deployment.

  • Configuration via Options All configurable elements use IOptions<T> with validation to guarantee safe, predictable behavior.

  • Observability First Logging, tracing, and metrics are not optional β€” they are embedded in every template by default.

  • Accessibility Always Components must be WCAG-compliant, with ARIA roles, keyboard navigation, and focus handling.

  • Localization-Ready Every template includes IStringLocalizer support for multilingual portals.

  • Security by Design No component or shell feature can be exposed without CSP, sanitization, and hardened defaults.


🌍 Layered Architecture

The templates enforce a layered structure for maintainability and clarity:

graph TD
  UI[🎨 UI Layer - Razor Components, Scoped CSS, Tokens]
  State[🧩 State Layer - Fluxor/Redux-like Store]
  Integration[πŸ”Œ Integration Layer - BFF, YARP, ECS, SignalR]
  Backend[πŸ“¦ Backend - Microservices, APIs, ECS]

  UI --> State --> Integration --> Backend
Hold "Alt" / "Option" to enable pan & zoom
  • UI Layer β†’ Tailwind tokens + scoped styles, reusable Razor components.
  • State Layer β†’ predictable, unidirectional data flow for reliable UI updates.
  • Integration Layer β†’ HttpClient (BFF), ECS feature flags, SignalR subscriptions.
  • Backend β†’ microservices and ECS policies consumed through standardized APIs.

🚫 Anti-Patterns

To avoid fragmentation, the following are explicitly forbidden:

  • ❌ Hardcoded UI kit dependencies inside core templates.
  • ❌ Direct backend calls bypassing the BFF/YARP layer.
  • ❌ Inline CSS that bypasses tokens or theme system.
  • ❌ Embedding domain/business logic inside UI components.
  • ❌ Missing observability anchors (logs, metrics, traces).

βœ… Guarantees

  • Every template enforces these principles automatically at generation.
  • All downstream implementations inherit localization, accessibility, observability, and security by default.
  • Anti-patterns are detectable and preventable through linting and QA pipelines.
  • Regeneration remains idempotent β€” principles are reapplied consistently across updates.

πŸ“› Template Identity & Catalog Registration

🎯 Purpose

Define the unique identity, naming conventions, and catalog metadata for all ConnectSoft Blazor templates. This section ensures every template is traceable, discoverable, and consistently generated inside the Factory.

πŸ’‘ Identity is not cosmetic β€” it’s the anchor that drives catalog resolution, namespace mapping, and long-term traceability.


πŸ†” Identity Model

Each template is uniquely defined by:

  • Template ID β€” canonical identifier in the catalog (connectsoft.blazor.component-lib).
  • Trace ID β€” globally unique ID used across the Factory for versioning and memory embedding.
  • Namespace Root β€” .NET namespace prefix derived from solution/project scope.
  • Tags β€” key labels for grouping (e.g., frontend, blazor, saas, component).
  • Versioning β€” semantic version alignment with Factory releases.

🧩 Catalog Registration

Templates are registered in the ConnectSoft Template Catalog:

  • Catalog Entry Metadata

    • Template ID
    • Short/long description
    • Default project structure
    • Supported options/parameters
  • Discovery & Installation

    • Published to NuGet + internal feed
    • Installable via dotnet new
    • Searchable by ID, tags, or domain context
  • Consistency Rules

    • IDs must be globally unique
    • Namespaces must match project and domain context
    • Version increments must align with blueprint evolution

πŸ“‚ Example Identity Block

{
  "templateId": "connectsoft.blazor.appshell",
  "traceId": "tpl-20250905-blazor-appshell-v1",
  "namespaceRoot": "ConnectSoft.Blazor.AppShell",
  "tags": ["frontend", "blazor", "portal", "saas"],
  "version": "1.0.0"
}

πŸ“‚ Example Catalog Manifest

templates:
  - id: connectsoft.blazor.component-lib
    description: "Reusable Razor Class Library with Tailwind tokens"
    tags: [frontend, blazor, rcl]
    namespace: ConnectSoft.Blazor.ComponentLib
    version: 1.0.0
  - id: connectsoft.blazor.microfrontend
    description: "Blazor WASM packaged as Custom Element"
    tags: [frontend, blazor, mfe]
    namespace: ConnectSoft.Blazor.Microfrontend
    version: 1.0.0
  - id: connectsoft.blazor.appshell
    description: "Blazor Web App shell for SaaS portals"
    tags: [frontend, blazor, portal, saas]
    namespace: ConnectSoft.Blazor.AppShell
    version: 1.0.0

βœ… Guarantees

  • Every template has a single source of truth identity block.
  • IDs, namespaces, and trace IDs are immutable once generated.
  • Catalog registration ensures discoverability and reuse across all Factory projects.
  • Versioning is semantic and traceable, allowing deterministic regeneration.

πŸ“¦ Repository & Packaging Topology

🎯 Purpose

Define a multi-repository topology where each template (Component Library, Microfrontend Library, Application Shell) lives in its own dedicated repo, with a lightweight Catalog repo to coordinate discovery, cross-cutting standards, and releases.

πŸ’‘ Separate repos = clean ownership, clearer permissions, faster CI, and independent release trains β€” without losing a unified catalog.


πŸ—‚οΈ Repositories

1) connectsoft.blazor.component-library Reusable RCL with Tailwind tokens; bUnit tests; NuGet packaging.

2) connectsoft.blazor.microfrontend Blazor WASM β†’ Custom Element packaging; CDN manifest; integration tests.

3) connectsoft.blazor.appshell Blazor Web App shell; BFF/YARP; SignalR; feature flags; Playwright/E2E.

4) connectsoft.blazor.catalog (coordination repo) Template Catalog (IDs, metadata), shared CI templates, analyzers, ADR index, release notes, documentation site.

πŸ“Œ Each functional repo remains autonomous; the Catalog repo provides the β€œsingle pane of glass.”


πŸ“ Directory Conventions (per repo)

root/
β”œβ”€β”€ src/
β”‚   └── <ProductName>/                 # Primary library/app
β”œβ”€β”€ samples/
β”‚   └── <ProductName>.Samples/         # Minimal runnable showcase
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ <ProductName>.UnitTests/       # bUnit/xUnit for libs
β”‚   └── <ProductName>.E2E/             # Playwright for shell; MFE host tests
β”œβ”€β”€ build/
β”‚   β”œβ”€β”€ versioning.props               # SemVer, source link
β”‚   └── pipelines/                     # Reusable YAML fragments
└── docs/
    β”œβ”€β”€ adr/                           # Architecture Decisions
    └── site/                          # Local docs (synced to Catalog)

🧱 Build / Pack / Publish Strategy

Build

  • .NET SDK pinned via global.json.
  • Roslyn analyzers + style rules from Catalog repo (consumed as a submodule or NuGet).
  • Deterministic builds, source link enabled.

Pack

  • dotnet pack produces signed .nupkg (Component + Shell) and tool/template packs.
  • Microfrontend builds emit CDN bundle + microfrontend.json (SRI + integrity).

Publish

  • NuGet: internal Azure Artifacts; optional NuGet.org for stabilized releases.
  • CDN: MFE bundles + manifests pushed to CDN buckets with immutable hashes.
  • Docs: repo-level docs synced as a subtree into Catalog for the central site.

πŸ’‘ MFE’s dual-output is critical: a NuGet developer package and a CDN runtime bundle.


πŸ”„ Release Channels & Versioning

  • Channels: alpha (feature dev), beta (API stable, UX evolving), stable (API & UX locked).
  • Tags: vX.Y.Z[-channel] on each repo.
  • Semantic Versioning:
    • Major: breaking template scaffolds/parameters.
    • Minor: new features, non-breaking options/components.
    • Patch: fixes, docs, perf, a11y improvements.

Cross-Repo Coordination (Catalog-driven):

  • Catalog repo holds a Release Matrix mapping compatible versions:

appshell >=1.2.0
microfrontend >=1.1.0
component-library >=1.0.5
* PR checks in each repo validate against the matrix to prevent drift.


🧰 Shared Governance (from Catalog repo)

  • Reusable YAML pipelines imported into each repo:
    • build.yml (restore, build, test, pack)
    • quality.yml (lint, analyzers, coverage gates)
    • security.yml (SBOM, vuln scan)
    • release.yml (tag, publish, notify)
  • Analyzer package: ConnectSoft.CodeQuality (ruleset, editorconfig, banned APIs).
  • Template catalog manifest: IDs, descriptions, tags, default parameters, sample links.

πŸ’‘ Keep shared assets versioned in the Catalog as packages; repos consume pinned versions to avoid accidental coupling.


πŸ§ͺ CI Stages (per repo)

  1. Validate: restore, build, unit tests (bUnit/xUnit).
  2. Integration: Playwright (Shell), MFE host smoke tests, API contract checks (if any).
  3. Security: SBOM, dependency scan, CSP/SRI validation (for MFE).
  4. Package: produce .nupkg, site.zip, bundle.zip.
  5. Publish: push to feed/CDN; create GitHub/Azure DevOps release; update Catalog via PR.

πŸ“¦ Example Outputs

Component Library

packages/ConnectSoft.Blazor.ComponentLibrary.1.0.0.nupkg
symbols/ConnectSoft.Blazor.ComponentLibrary.1.0.0.snupkg
docs/site.zip

Microfrontend Library

packages/ConnectSoft.Blazor.Microfrontend.1.0.0.nupkg
cdn/billing-mfe.1.0.0/{app.js, app.wasm, assets/*}
cdn/microfrontend.json   # name, version, element, integrity, capabilities

Application Shell

packages/ConnectSoft.Blazor.AppShell.1.0.0.nupkg
playwright-report/*
docs/site.zip

πŸ” Access & Branching Policy

  • Branching: main (protected), release/*, feature/*.
  • Protection: required reviews, status checks (quality, security, coverage).
  • Permissions: per-repo code owners (UI, MFE, Shell), Catalog owners for shared assets.
  • Tokens/Secrets: per-repo Key Vault/Variable Groups; no shared secrets across repos.

πŸ”— Catalog Sync Flow

  1. Template repo releases (v1.0.0).
  2. Pipeline opens PR in Catalog to update:
    • catalog manifest (IDs, versions, tags)
    • compatibility matrix
    • docs navigation + sample links
  3. Catalog CI builds the central docs site and publishes the updated index.

πŸ“Œ The Catalog PR is the single source of truth for consumers β€” if it’s not in Catalog, it’s not official.


βœ… Guarantees

  • Each template is isolated and independently releasable.
  • Cross-cutting quality and security are enforced via shared, versioned tooling.
  • The Catalog provides a unified discovery layer without reintroducing monorepo coupling.
  • All artifacts (NuGet, CDN bundles, docs) are reproducible and traceable to a tag.

🎨 Design Tokens & Tailwind Preset

🎯 Purpose

Define a shared, UI-kit-agnostic design system delivered as CSS variables + Tailwind preset, consumed by all three templates (Component Library, Microfrontend, Application Shell). This ensures consistent theming (light/dark/density), accessibility, and brand cohesion across the Factory.

πŸ’‘ Tokens are the single source of visual truthβ€”components render tokens, adapters style behaviors.


🧬 Token Taxonomy

Foundations (raw scales)

  • color: --cs-color-* (brand, neutral, accent, success, warning, danger)
  • space: --cs-space-* (0, 0.5, 1, 1.5, 2, 3, 4, 6, 8, 12)
  • size: --cs-size-* (2, 4, 6, 8, 10, 12, 14, … 64)
  • radius: --cs-radius-* (none, sm, md, lg, xl, 2xl)
  • elevation: --cs-shadow-* (0..5)
  • opacity: --cs-opacity-*
  • z: --cs-z-* (dropdown, modal, toast, overlay)

Typography

  • font.family: --cs-font-sans, --cs-font-mono
  • font.size: --cs-text-* (xs..9xl)
  • font.weight: --cs-weight-* (regular..black)
  • line.height: --cs-leading-*
  • letter.spacing: --cs-tracking-*

Semantic aliases (theme-aware)

  • surface.*: background layers (--cs-surface-1/2/3)
  • content.*: text levels (--cs-content-hi/lo/inverse)
  • border.*: --cs-border-muted/strong/focus
  • action.*: --cs-action-fg/bg/hover/active/disabled
  • status.*: --cs-status-success/warning/danger/info

Behavioral

  • focus: ring width, color, offset
  • motion: durations/easings for enter/exit
  • density: compact / cozy / comfortable paddings & heights

πŸ“Œ Components should bind only to semantic aliases (e.g., --cs-action-bg)β€”never raw brand colors.


πŸ“ Source Layout (shared package)

connectsoft.theme.tokens/
β”œβ”€β”€ css/
β”‚   β”œβ”€β”€ tokens.css              # CSS variables (light theme baseline)
β”‚   β”œβ”€β”€ tokens.dark.css         # Dark-mode overrides
β”‚   β”œβ”€β”€ tokens.density.css      # Compact/cozy/comfortable presets
β”‚   └── tokens.a11y.css         # High-contrast & focus utilities
β”œβ”€β”€ tailwind/
β”‚   β”œβ”€β”€ preset.cjs              # Tailwind preset mapping β†’ CSS vars
β”‚   └── plugins.cjs             # Utilities (focus-ring, elevation)
└── docs/
    └── tokens-map.md

🧩 Tailwind Preset Mapping

tailwind/preset.cjs (excerpt)

module.exports = {
  darkMode: ["class", '[data-theme="dark"]'],
  theme: {
    extend: {
      colors: {
        surface: {
          1: "rgb(var(--cs-surface-1) / <alpha-value>)",
          2: "rgb(var(--cs-surface-2) / <alpha-value>)",
          3: "rgb(var(--cs-surface-3) / <alpha-value>)",
        },
        content: {
          DEFAULT: "rgb(var(--cs-content-hi) / <alpha-value>)",
          muted: "rgb(var(--cs-content-lo) / <alpha-value>)",
          inverse: "rgb(var(--cs-content-inverse) / <alpha-value>)",
        },
        action: {
          bg: "rgb(var(--cs-action-bg) / <alpha-value>)",
          fg: "rgb(var(--cs-action-fg) / <alpha-value>)",
          hover: "rgb(var(--cs-action-hover) / <alpha-value>)",
        },
        status: {
          success: "rgb(var(--cs-status-success) / <alpha-value>)",
          warning: "rgb(var(--cs-status-warning) / <alpha-value>)",
          danger:  "rgb(var(--cs-status-danger)  / <alpha-value>)",
          info:    "rgb(var(--cs-status-info)    / <alpha-value>)",
        },
      },
      spacing: {
        'xs': 'var(--cs-space-1)',
        'sm': 'var(--cs-space-2)',
        'md': 'var(--cs-space-3)',
        'lg': 'var(--cs-space-4)',
        'xl': 'var(--cs-space-6)'
      },
      borderRadius: {
        none: 'var(--cs-radius-none)',
        sm: 'var(--cs-radius-sm)',
        md: 'var(--cs-radius-md)',
        lg: 'var(--cs-radius-lg)',
        xl: 'var(--cs-radius-xl)',
        '2xl': 'var(--cs-radius-2xl)',
      },
      boxShadow: {
        cs0: 'var(--cs-shadow-0)',
        cs1: 'var(--cs-shadow-1)',
        cs2: 'var(--cs-shadow-2)',
        cs3: 'var(--cs-shadow-3)',
        cs4: 'var(--cs-shadow-4)',
        cs5: 'var(--cs-shadow-5)',
      }
    }
  },
  plugins: [
    require('./plugins.cjs') // focus ring, elevation, a11y helpers
  ]
};

🧾 CSS Variables (light baseline & dark overrides)

css/tokens.css (excerpt)

:root {
  /* brand/neutral (as RGB triplets for alpha control) */
  --cs-color-brand-600: 20 115 230;
  --cs-color-neutral-900: 17 24 39;

  /* semantic surfaces & content */
  --cs-surface-1: 255 255 255;
  --cs-surface-2: 249 250 251;
  --cs-content-hi: 17 24 39;
  --cs-content-lo: 75 85 99;

  /* actions */
  --cs-action-bg: var(--cs-color-brand-600);
  --cs-action-fg: 255 255 255;
  --cs-action-hover: 16 98 196;

  /* status */
  --cs-status-success: 16 185 129;
  --cs-status-warning: 245 158 11;
  --cs-status-danger: 239 68 68;
  --cs-status-info: 59 130 246;

  /* spacing/radius */
  --cs-space-1: .25rem; --cs-space-2: .5rem; --cs-space-3: .75rem; --cs-space-4: 1rem; --cs-space-6: 1.5rem;
  --cs-radius-sm: .25rem; --cs-radius-md: .375rem; --cs-radius-lg: .5rem; --cs-radius-xl: .75rem; --cs-radius-2xl: 1rem;

  /* focus */
  --cs-focus-ring: 59 130 246; /* blue-500 */
  --cs-focus-width: 2px;
}

css/tokens.dark.css (excerpt)

:root[data-theme="dark"], .dark {
  --cs-surface-1: 17 24 39;
  --cs-surface-2: 31 41 55;
  --cs-content-hi: 243 244 246;
  --cs-content-lo: 209 213 219;
  --cs-action-fg: 17 24 39; /* ensure contrast for brand bg */
}

⚠️ High-contrast variants live in tokens.a11y.css. Apply via [data-a11y="high-contrast"].


🧰 Consumption Patterns

Component Library (RCL)

  • Imports preset in tailwind.config.cjs: presets: [require('@connectsoft/theme/tailwind/preset.cjs')]
  • Razor components use semantic classes: bg-surface-1 text-content p-sm rounded-md shadow-cs1
  • No hard-coded colors; behavior visuals via adapters.

Microfrontend (WASM CE)

  • Shadow-DOM aware styles: expose token scope on host; provide :host mappings.
  • Respect shell-provided theme via data-theme and density via data-density.
  • Avoid global overrides; mount styles inside CE boundary.

Application Shell

  • Owns theme selection (light/dark/a11y) and density; sets data attributes on <html> or root layout.
  • Persists preferences per-tenant; broadcasts theme changes to MFEs.

πŸ§ͺ Validation & Quality Gates

  • Contrast checks (WCAG AA/AAA) automated against semantic pairs (surface.* Γ— content.*, action.bg Γ— action.fg).
  • Token drift detector: CI step verifying no component uses raw hex/RGB values.
  • Preset integrity: snapshot test for Tailwind theme map β†’ CSS vars.
  • Bundle bloat guard: ensure token CSS ≀ target size (e.g., 8KB gz).

πŸ’‘ If a component needs a color not in tokensβ€”that’s a token bug, not a component exception.


πŸ” Theming & Density Contract

  • Theme switching via data-theme="light|dark" and optional data-a11y="high-contrast".
  • Density via data-density="compact|cozy|comfortable" adjusts paddings/heights using --cs-space-*.
  • MFEs must listen and re-render on attribute changes (MutationObserver or event).

πŸ“¦ Distribution

  • Publish as NuGet for .NET consumers (embed CSS files + static web assets).
  • Publish as NPM (optional) for cross-ecosystem use (future-proofing).
  • Version tokens independently (connectsoft.theme.tokens@X.Y.Z) with documented breaking-change policy.

βœ… Guarantees

  • One token source of truth drives all visuals across RCL, MFEs, and Shell.
  • UI kits (Flowbite, DaisyUI, etc.) are plugged via adapters, never altering tokens.
  • Accessibility and theming are first-class, not afterthoughts.
  • Regeneration is idempotentβ€”same inputs produce the same preset maps.

🧱 Component Library Template

🎯 Purpose

Ship a Blazor Class Library (RCL) template that provides a tokens-first component set with scoped CSS, accessibility baked-in, and a MSTest harness (unit + rendering tests). This library becomes the foundation design system for microfrontends and shellsβ€”UI-kit-agnostic and ready for NuGet distribution.

πŸ’‘ Components render tokens; behavior is pluggable via adapters. No kit lock-in.


πŸ“¦ Template Output

  • Template ID: connectsoft.blazor.component-lib
  • Project Name: ConnectSoft.Blazor.ComponentLibrary
  • Artifacts: .nupkg with static web assets (tokens CSS), XML docs, SourceLink, symbols
  • Scaffold: sample components (Button, TextField, Modal), docs snippets, tests

πŸ“ Project Layout (generated)

src/
  ConnectSoft.Blazor.ComponentLibrary/
    Components/
      Button.razor
      Button.razor.css
      TextField.razor
      TextField.razor.css
      Modal.razor
      Modal.razor.css
    Accessibility/
      FocusRing.razor.css
      Aria.cs
    Adapters/
      IUiKitAdapter.cs
      TokensAdapter.cs            // default no-kit adapter
    Options/
      UiOptions.cs
      UiOptionsValidator.cs
    Localization/
      Resources.resx
      Resources.ru.resx
      Resources.he.resx
    wwwroot/
      css/tokens.css              // imported from tokens package at pack time
    ConnectSoft.Blazor.ComponentLibrary.csproj
tests/
  ConnectSoft.Blazor.ComponentLibrary.Tests/
    ButtonTests.cs
    TextFieldTests.cs
    ModalTests.cs
    TestHost.cs                   // bUnit + MSTest integration
template/
  .template.config/template.json  // dotnet new metadata & parameters

🧱 Baseline Components (v1)

  • Button: sizes (sm, md, lg), variants (primary, secondary, subtle, danger), loading, disabled, icon-before/after, full-width
  • TextField: label, helper/error text, prefix/suffix slots, validation states, required marker
  • Modal: header/body/footer slots, trap focus, ESC/overlay close, size presets

πŸ“Œ All variants/style states are driven by semantic tokens (e.g., --cs-action-bg).


🎨 Styling & Accessibility

  • Scoped CSS per component (.razor.css), zero global bleed
  • Tokens-first utility classes (bg-surface-1 text-content rounded-md) via Tailwind preset
  • A11y: ARIA roles/labels, keyboard navigation, visible focus ring, motion-respecting transitions
  • Density & Theme: respect data-density and data-theme attributes propagated from host

Example: Button.razor (excerpt)

<button
  class="cs-btn inline-flex items-center justify-center rounded-md px-sm py-xs
         bg-[color:rgb(var(--cs-action-bg))] text-[color:rgb(var(--cs-action-fg))]
         hover:bg-[color:rgb(var(--cs-action-hover))] focus:outline-none
         focus:ring-2 focus:ring-offset-2 focus:ring-[color:rgb(var(--cs-focus-ring))]"
  disabled="@Disabled"
  aria-disabled="@Disabled"
  @onclick="OnClick">
  @if (IconBefore is not null) { <span class="mr-xs">@IconBefore</span> }
  <span>@ChildContent</span>
  @if (IconAfter is not null) { <span class="ml-xs">@IconAfter</span> }
</button>

@code {
  [Parameter] public bool Disabled { get; set; }
  [Parameter] public RenderFragment? IconBefore { get; set; }
  [Parameter] public RenderFragment? IconAfter { get; set; }
  [Parameter] public RenderFragment ChildContent { get; set; } = default!;
  [Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
}

🧰 Options & Localization

  • Options: UiOptions (density default, focus ring mode, animation enablement) with DataAnnotations validator
  • Localization: IStringLocalizer wired; RESX for built-in strings (e.g., β€œClose”, β€œRequired”)
public sealed class UiOptions
{
  [Required, RegularExpression("compact|cozy|comfortable")]
  public string DefaultDensity { get; init; } = "cozy";

  public bool ReducedMotion { get; init; } = false;
}

πŸ”Œ Adapter Surface (no kit lock-in)

public interface IUiKitAdapter
{
  RenderFragment Toast(string message, UiSeverity severity);
  RenderFragment ModalFrame(RenderFragment content);
  RenderFragment ValidationMessage(string text);
}
  • Default: TokensAdapter (pure tokens)
  • Extensible: optional FlowbiteAdapter or others in sample repo (not a core dependency)

πŸ”¬ Testing (MSTest + bUnit)

  • Framework: MSTest V2 with bUnit for component rendering
  • Coverage: rendering states, events, accessibility (role/label), focus trap for Modal
  • Snapshots: optional structural snapshots (pragma kept small to avoid fragility)
[TestClass]
public class ButtonTests : BunitTestContext
{
  [TestMethod]
  public void Renders_Primary_Button_With_Text()
  {
    var cut = RenderComponent<Button>(p => p.AddChildContent("Save"));
    cut.MarkupMatches(@"<button ...>Save</button>");
  }
}

πŸ§ͺ Quality Gates

  • No raw colors in components (CI token drift rule)
  • Axe/Pa11y checks for sample gallery (contrast, roles)
  • Analyzer pack: disallow synchronous JS interop, enforce nullable, logging categories
  • API surface: public components documented with XML; breaking changes tracked with API diff

βš™οΈ Packaging & Static Web Assets

  • Static Web Assets: wwwroot/css/tokens.css embedded; consumers get tokens automatically
  • SourceLink & Symbols: enabled for NuGet packages
  • Minimal Dependencies: keep transitive graph tight; adapters shipped separately

  • Renders all states/variants with copyable code
  • Theme & density switchers (light/dark/high-contrast, compact/cozy/comfortable)
  • A11y panel: tab order, focus outlines, screen-reader labels

πŸ’‘ Gallery is your living spec. If a state is missing in the gallery, it doesn’t exist in the design system.


βœ… Guarantees

  • Tokens-first, UI-kit-agnostic components with scoped CSS
  • Accessible by default (roles, keyboard, focus)
  • MSTest + bUnit harness ensures render correctness and regression safety
  • Packaged as NuGet with static assets, ready for immediate reuse

πŸ”Œ UI Kit Adapter Contracts

🎯 Purpose

Define a stable abstraction that decouples visual behavior (dialogs, toasts, validation, menus, overlays) from visual tokens (colors, spacing). Components render tokens-first; adapters implement behaviors using a concrete UI kit (Flowbite, DaisyUI, etc.) or a pure-tokens default.

πŸ’‘ Adapters let product teams pick or swap a UI kit at the application/shell layer β€” without touching component code.


πŸ”§ Contract Goals

  • Stable interfaces: minimal, composable, versioned.
  • No hard dependency on any UI kit in the core library.
  • Pluggable via DI at app/shell level (IUiKitAdapter).
  • SSR/WASM-safe: avoid assumptions that break in prerender/WASM.
  • A11y-first: adapters must preserve ARIA, focus management, and keyboard flows.

🧩 Core Interfaces

Base marker & capabilities

public interface IUiKitAdapter { }

[Flags]
public enum UiKitCapabilities
{
  None        = 0,
  Dialogs     = 1 << 0,
  Toasters    = 1 << 1,
  Dropdowns   = 1 << 2,
  Tooltips    = 1 << 3,
  Validation  = 1 << 4,
  Tabs        = 1 << 5
}

Dialog & Overlay

public interface IUiDialogAdapter : IUiKitAdapter
{
  // Creates a modal frame that traps focus and respects ESC/overlay close
  RenderFragment Frame(RenderFragment content, DialogOptions? options = null);

  // Optional programmatic API (shell may call)
  Task ShowAsync(RenderFragment content, DialogOptions? options = null, CancellationToken ct = default);
  Task CloseAsync(CancellationToken ct = default);
}

public sealed record DialogOptions(
  string? Title = null,
  bool CloseOnOverlay = true,
  string Size = "md",         // sm|md|lg|xl
  bool InitialFocus = true);

Toasts / Notifications

public interface IUiToastAdapter : IUiKitAdapter
{
  RenderFragment Inline(string message, UiSeverity severity = UiSeverity.Info, ToastOptions? options = null);
  Task ShowAsync(string message, UiSeverity severity = UiSeverity.Info, ToastOptions? options = null, CancellationToken ct = default);
}

public enum UiSeverity { Info, Success, Warning, Danger }

public sealed record ToastOptions(
  TimeSpan? Duration = null,
  bool Closable = true,
  string? IconClass = null);

Dropdowns, Menus, Tooltips (optional)

public interface IUiMenuAdapter : IUiKitAdapter
{
  RenderFragment Trigger(RenderFragment button, string id);
  RenderFragment Content(RenderFragment items, string id, MenuOptions? options = null);
}

public sealed record MenuOptions(bool CloseOnSelect = true);

Validation Rendering

public interface IUiValidationAdapter : IUiKitAdapter
{
  RenderFragment Message(string text, UiSeverity severity = UiSeverity.Danger);
  string InputValidClass { get; }
  string InputInvalidClass { get; }
}

πŸ“Œ Keep interfaces small and orthogonal. New behaviors β†’ new interfaces, not new methods on old ones.


🧱 Default Adapter (Tokens-Only)

TokensAdapter ships in the component library to provide zero-dependency behaviors using tokens + minimal JS (if any).

public sealed class TokensDialogAdapter : IUiDialogAdapter
{
  public RenderFragment Frame(RenderFragment content, DialogOptions? options = null) => builder =>
  {
    // Backdrop + container using tokenized classes
    builder.OpenElement(0, "div");
    builder.AddAttribute(1, "class", "fixed inset-0 bg-[color:rgb(var(--cs-content-lo))]/40");
    builder.OpenElement(2, "div");
    builder.AddAttribute(3, "class", "fixed inset-0 flex items-center justify-center p-lg");
    builder.OpenElement(4, "div");
    builder.AddAttribute(5, "class", "bg-surface-1 rounded-lg shadow-cs4 w-full max-w-xl");
    if (!string.IsNullOrWhiteSpace(options?.Title))
    {
      builder.OpenElement(6, "div");
      builder.AddAttribute(7, "class", "px-lg pt-lg text-content font-medium");
      builder.AddContent(8, options!.Title);
      builder.CloseElement();
    }
    builder.OpenElement(9, "div");
    builder.AddAttribute(10, "class", "p-lg");
    builder.AddContent(11, content);
    builder.CloseElement(); // body
    builder.CloseElement(); // panel
    builder.CloseElement(); // center
    builder.CloseElement(); // backdrop
  };

  public Task ShowAsync(RenderFragment content, DialogOptions? options = null, CancellationToken ct = default) 
    => Task.CompletedTask;
  public Task CloseAsync(CancellationToken ct = default) => Task.CompletedTask;
}
  • Focus trap: implemented via a small JS module (tokens-only) or Blazor focus APIs.
  • Animations: respect prefers-reduced-motion and UiOptions.ReducedMotion.

🌊 Flowbite Adapter (example package)

A separate, optional package: ConnectSoft.Blazor.UiKit.FlowbiteAdapter (not referenced by core).

public sealed class FlowbiteDialogAdapter : IUiDialogAdapter
{
  public RenderFragment Frame(RenderFragment content, DialogOptions? options = null) => @<FlowbiteModal Size="@(options?.Size ?? "md")">
      @content
    </FlowbiteModal>;

  public Task ShowAsync(RenderFragment content, DialogOptions? options = null, CancellationToken ct = default) 
    => FlowbiteInterop.OpenAsync(/*...*/);

  public Task CloseAsync(CancellationToken ct = default)
    => FlowbiteInterop.CloseAsync(/*...*/);
}
  • Maps dialog/menu/tooltip to Flowbite components.
  • Respects semantic tokens for colors via CSS var bridge where possible.
  • Ships no tokens, only behavior mappings.

🌼 DaisyUI Adapter (example package)

ConnectSoft.Blazor.UiKit.DaisyAdapter:

  • Uses DaisyUI classes for frames, alerts, menus.
  • Adds a tiny bridge stylesheet mapping semantic tokens β†’ Daisy classes when needed.
  • Ensures keyboard & ARIA parity with the default adapter.

🧰 Registration & Selection (DI)

App/Shell registration

services.AddSingleton<IUiDialogAdapter, TokensDialogAdapter>();
services.AddSingleton<IUiToastAdapter, TokensToastAdapter>();
services.AddSingleton<IUiValidationAdapter, TokensValidationAdapter>();

// Swap to Flowbite
// services.Replace(ServiceDescriptor.Singleton<IUiDialogAdapter, FlowbiteDialogAdapter>());
// services.Replace(ServiceDescriptor.Singleton<IUiToastAdapter, FlowbiteToastAdapter>());
// services.Replace(ServiceDescriptor.Singleton<IUiValidationAdapter, FlowbiteValidationAdapter>());

Component consumption

@inject IUiDialogAdapter Dialog
@code {
  void Open() => Dialog.ShowAsync(builder => @<div>Settings</div>);
}

πŸ’‘ The shell decides the adapter set. MFEs inherit through DI when hosted in the shell.


πŸ§ͺ Testing & Compliance

  • Adapter conformance tests in an Adapter Test Kit project:
    • Focus trap, ESC/overlay close, role/label expectations.
    • Toast lifetimes & stacking.
    • Validation classes applied on success/error.
  • A11y checks (axe) against the same spec for tokens/Flowbite/Daisy adapters.
  • Snapshot deltas allowed per adapter (visuals differ), behavior must match.

πŸ” Security & Performance

  • No untrusted HTML injection.
  • CSP-safe: adapters must work without unsafe-inline (use classes/attributes).
  • No long-running JS timers; toasts use requestAnimationFrame or CSS animation events.
  • Keep adapter bundles small; avoid bringing entire kit if only 1–2 behaviors used.

πŸ” Versioning & Evolution

  • Interfaces follow semantic versioning.
  • New behaviors => new interfaces or optional methods with defaults.
  • Breaking changes batched into major updates and accompanied by adapter migration notes.

βœ… Guarantees

  • Components stay UI-kit-agnostic; behavior is pluggable.
  • Shells can swap adapters without recompiling component packages.
  • Accessibility & keyboard/focus behaviors remain consistent across adapters.
  • No core dependency on Flowbite/Daisy β€” adapters are optional extensions.

🧩 Advanced Component Library Features

🎯 Purpose

Elevate the Component Library beyond visuals by standardizing options binding, localization (i18n), accessibility verification, and state conventions. This ensures every component behaves predictably in enterprise SaaS portals (multi-tenant, multilingual, accessible-by-default).

πŸ’‘ Visuals attract. Predictability retains. These features turn a pretty kit into a production system.


βš™οΈ Options Binding (strongly-typed, validated)

Goals

  • Centralize component-wide defaults (density, motion, focus, validation mode).
  • Guarantee safe configuration with validation and live updates.

Contracts

public sealed class UiOptions
{
  [Required, RegularExpression("compact|cozy|comfortable")]
  public string DefaultDensity { get; init; } = "cozy";

  public bool ReducedMotion { get; init; } = false;

  [Required, RegularExpression("inline|tooltip|none")]
  public string ValidationPresentation { get; init; } = "inline";
}

public static class ServiceCollectionExtensions
{
  public static IServiceCollection AddConnectSoftUi(this IServiceCollection services, IConfiguration cfg)
  {
    services.AddOptions<UiOptions>()
            .Bind(cfg.GetSection("Ui"))
            .ValidateDataAnnotations()
            .ValidateOnStart();
    return services;
  }
}

Patterns

  • Use IOptionsSnapshot<UiOptions> in server contexts (changes per-request).
  • Use IOptionsMonitor<UiOptions> in WASM for runtime updates (theme/density toggles).
  • Expose component-level overrides via parameters; merge with UiOptions on render.

πŸ“Œ Options govern behavior defaults; parameters allow local overrides without forking patterns.


🌐 Localization & Globalization (i18n/L10n)

Goals

  • First-class multilingual support (EN, RU, HE) with RTL awareness.
  • Locale-aware formatting (dates, numbers) and pluralization.

Contracts

// In library startup (sample)
services.AddLocalization(options => options.ResourcesPath = "Localization");

// Component usage
@inject IStringLocalizer<Resources> T
<span aria-label="@T["Close"]">@T["Close"]</span>

Guidelines

  • Ship neutral Resources.resx + culture-specific .resx (e.g., Resources.ru.resx, Resources.he.resx).
  • Support RTL via [dir="rtl"] detection and tokens that flip paddings/margins logically (start/end).
  • Use CultureInfo.CurrentUICulture for number/date formats.
  • Provide Plural helper:
public static string Plural(this IStringLocalizer T, string key, int count)
  => T[$"{key}_{(count == 1 ? "One" : "Other")}", count];

Testing

  • Snapshot critical components in at least two languages and LTR/RTL.
  • Validate visual overflow for long translations (German-like length).

πŸ’‘ If you can’t switch to HE (RTL) in the gallery and keep layout intact, it’s not done.


β™Ώ Accessibility Checks (a11y)

Goals

  • Enforce WCAG AA at component level: contrast, keyboard, roles, focus order.

Baseline

  • Every interactive component must:

  • have a visible focus indicator (token-driven),

  • support keyboard navigation (Tab/Shift+Tab, Arrow for lists/menus),
  • include proper roles/ARIA (role="dialog", aria-modal="true", aria-expanded, aria-controls),
  • support labels & descriptions (aria-label, aria-labelledby, aria-describedby).

Automation

  • Run axe against the Sample Gallery routes in CI.
  • Pa11y smoke checks for high-level flows (open/close Modal, error states).
  • Contrast tests for semantic pairs (content vs surface, action.fg vs action.bg).

Contract Snippet

<div role="dialog" aria-modal="true" aria-labelledby="@TitleId" aria-describedby="@BodyId">
  <h2 id="@TitleId">@T["Settings"]</h2>
  <div id="@BodyId">@ChildContent</div>
</div>

πŸ“Œ Accessibility is not a lint ruleβ€”it's a behavior contract. Tests must prove it.


πŸ”„ State Conventions (controlled/uncontrolled, events, async)

Goals

  • Predictable component behavior under different ownership models.
  • Consistent event naming, cancellation, and async handling.

Patterns

  1. Controlled vs Uncontrolled

  2. If a component’s visibility/value can be externally driven, expose:

    • bool? IsOpen + EventCallback<bool> IsOpenChanged
    • TValue? Value + EventCallback<TValue> ValueChanged
    • If IsOpen/Value is not provided, manage internal state (uncontrolled).
  3. Event Model

  4. Use EventCallback for UI events (OnClick, OnChange).

  5. Provide cancellable events via BeforeXxxAsync(CancellableArgs args).
  6. Provide completion events via AfterXxxAsync(EventArgs args).
public sealed class CancellableArgs : EventArgs
{
  public bool Cancel { get; set; }
  public string? Reason { get; set; }
}
  1. Async & Loading

  2. Public Task methods must be await-safe; prevent double-activation with an IsBusy token.

  3. Show skeletons or progress based on UiOptions.ReducedMotion.
  4. Ensure idempotency: repeated clicks don’t break state.

  5. Forms & Validation

  6. Integrate with EditForm + ValidationMessage<T> patterns.

  7. Expose ValidationClassProvider mapping for valid/invalid states (via IUiValidationAdapter).

Example (controlled modal)

@code {
  [Parameter] public bool? IsOpen { get; set; }
  [Parameter] public EventCallback<bool> IsOpenChanged { get; set; }

  private bool internalOpen;
  private bool Open => IsOpen ?? internalOpen;

  private async Task SetOpenAsync(bool value)
  {
    internalOpen = value;
    if (IsOpen.HasValue) await IsOpenChanged.InvokeAsync(value);
    StateHasChanged();
  }
}

πŸ’‘ Every interactive component should document both controlled and uncontrolled usage.


πŸ§ͺ Testing & Quality Gates

  • Unit: MSTest + bUnit for state transitions, two-way binding, cancellable events.
  • A11y: axe/Pa11y CI gates; fail build on violations (allow explicit, reviewed suppressions).
  • i18n: culture-switch tests for pluralization and RTL mirrors.
  • Options: configure with invalid values to assert validation failures at startup.
  • API Surface: XML docs completeness; public API diff checked per PR.

  • Controls panel per component: props, events, slots/child content.
  • Usage tabs: controlled/uncontrolled examples, form integration, i18n/RTL toggles.
  • A11y tab: keyboard map and roles/ARIA used.
  • Design tab: semantic token references (what colors/spacing it binds to).

πŸ” Security & Performance

  • Disallow raw MarkupString unless explicitly documented and sanitized.
  • No layout thrash: prefer CSS transitions; watch for forced reflows.
  • No unbounded timers or global event leaks; dispose subscriptions.

βœ… Guarantees

  • Behavior is configurable, localizable, and accessible by default.
  • State patterns are consistent across components (controlled/uncontrolled).
  • CI enforces a11y + i18n + options validation to keep quality non-negotiable.
  • Components remain UI-kit-agnostic and token-driven while offering rich behavior via adapters.

βš™οΈ Microfrontend Library Template

🎯 Purpose

Provide a Blazor WASM template that compiles into a Web Component (Custom Element), with a runtime bootstrap that’s shell-aware and a manifest stub for CDN distribution and policy enforcement. This enables domain teams to ship independent features that the portal shell can discover, load, and compose at runtime.

πŸ’‘ MFEs are product slices: build, version, and deploy them without touching the shell repo.


🧱 Template Output

  • Template ID: connectsoft.blazor.microfrontend
  • Project Name: ConnectSoft.Blazor.Microfrontend
  • Artifacts:

  • NuGet package (developer dependencies + helpers)

  • CDN bundle (WASM, JS boot, assets)
  • microfrontend.json manifest stub (capabilities, integrity, metadata)

πŸ“ Project Layout (generated)

src/
  ConnectSoft.Blazor.Microfrontend/
    App.razor
    Components/
      HostFrame.razor
    Bootstrap/
      MfeBootstrap.cs
      CustomElementRegistration.cs
      ShellBridge.cs
      ThemeAndCultureSync.cs
    Interop/
      dom.ts                    # CE registration & focus helpers
      loader.ts                 # manifest-aware loader
    Manifest/
      microfrontend.json        # emitted at build with placeholders
    wwwroot/
      entry.js                  # CE boot β†’ Blazor.start()
      styles.css
    ConnectSoft.Blazor.Microfrontend.csproj
tests/
  ConnectSoft.Blazor.Microfrontend.Tests/
    MountAsCustomElementTests.cs
    ThemeCulturePropagationTests.cs
    ManifestValidationTests.cs
template/
  .template.config/template.json

πŸš€ Runtime Bootstrap

Goals

  • Register the Blazor app as a Custom Element (e.g., <cs-mfe-billing>).
  • Support multiple instances on a page.
  • Be aware of shell context (tenant, edition, theme, culture).
  • Fail-safe when offline or capabilities are missing.

Key pieces

  • CustomElementRegistration.cs β†’ calls RootComponents.RegisterCustomElement<App>("cs-mfe-…").
  • entry.js β†’ CE lifecycle hooks (connectedCallback, disconnectedCallback) + Blazor.start().
  • ThemeAndCultureSync.cs β†’ listens for shell attribute changes (data-theme, lang) and applies to the root.
  • ShellBridge.cs β†’ optional events/commands channel initialized from element attributes.
// CustomElementRegistration.cs
public static class CustomElementRegistration
{
  public static void Register(WebAssemblyHostBuilder builder, string tag)
  {
    builder.RootComponents.RegisterCustomElement<App>(tag);
  }
}

🏷️ Custom Element Contract

Element tag

  • Default: cs-mfe-{kebab-name} (e.g., cs-mfe-billing)
  • Configurable via template parameter --element-tag.

Attributes

  • data-tenant / data-edition β€” bootstrap context
  • data-theme="light|dark" / data-density β€” visual state
  • lang="en|ru|he" β€” culture/UI language
  • data-endpoint β€” optional BFF endpoint root

πŸ“Œ All context attributes are optional; if not provided, MFE uses its defaults (UiOptions, neutral theme).


πŸ“‘ Manifest Stub (microfrontend.json)

Purpose: Tell the shell what this MFE is, what it needs, and how to load it.

{
  "name": "cs.billing",
  "displayName": "Billing",
  "version": "1.0.0",
  "element": "cs-mfe-billing",
  "entry": {
    "script": "https://cdn.example.com/cs.billing/1.0.0/entry.js",
    "wasm":   "https://cdn.example.com/cs.billing/1.0.0/app.wasm",
    "assets": [
      "https://cdn.example.com/cs.billing/1.0.0/styles.css"
    ],
    "integrity": {
      "entry.js": "sha384-…",
      "app.wasm": "sha384-…",
      "styles.css": "sha384-…"
    }
  },
  "capabilities": ["notify", "openBlade"],
  "permissions": {
    "endpoints": ["bff:/billing/*"]
  },
  "minShellVersion": "1.0.0",
  "i18n": ["en", "ru", "he"],
  "theming": ["light", "dark"],
  "density": ["compact", "cozy", "comfortable"]
}
  • Capabilities β†’ what the MFE can request from shell (notify, openBlade, …).
  • Permissions β†’ allowed BFF routes (shell will proxy/deny).
  • Integrity β†’ SRI hashes for secure loading.
  • minShellVersion β†’ composition gate in the shell registry.

πŸ”Œ Shell Awareness & Bridge

  • ShellBridge exposes lightweight APIs (events/commands):

    • Events: ThemeChanged, CultureChanged, TenantChanged, EditionChanged
    • Commands: Notify(string message, severity), OpenBlade(string id, object? args)
  • Communication mediums (in order of preference):

    1. Direct host API (shell sets a JS object on CE),
    2. DOM events (CustomEvent on CE),
    3. window.postMessage fallback (namespaced channel).

πŸ’‘ Bridge is optional β€” CE still runs standalone for local demos and unit tests.


🌐 i18n & Theme/Density Sync

  • Honor lang attribute on CE; set CultureInfo.CurrentUICulture.
  • Watch for data-theme and data-density changes (MutationObserver).
  • Provide helper events so the shell can broadcast changes at once.

πŸ§ͺ Testing

  • Mount tests: Custom Element attaches/detaches without leaks; multiple instances supported.
  • Manifest validation: schema validation + SRI presence.
  • Context propagation: theme/culture applied; UI re-renders on change.
  • Offline mode: graceful error if assets unavailable (renders fallback message).
[TestMethod]
public void Registers_Custom_Element_Tag()
{
  var tag = "cs-mfe-sample";
  var builder = WebAssemblyHostBuilder.CreateDefault();
  CustomElementRegistration.Register(builder, tag);
  // Assert: RootComponents contains tag mapping (pseudo-assertion).
}

πŸ—οΈ Build & Emit

  • Build produces:
    • entry.js + app.wasm + assets/* (hashed filenames recommended)
    • microfrontend.json populated from MSBuild variables (name, version, element, integrity)
  • Integrity hashes computed in CI; manifest updated post-build.

MSBuild knobs (sample)

<PropertyGroup>
  <MfeName>cs.billing</MfeName>
  <MfeElement>cs-mfe-billing</MfeElement>
  <MfeCdnRoot>$(CDN_ROOT)/cs.billing/$(Version)/</MfeCdnRoot>
</PropertyGroup>

πŸ” Security & Compliance

  • CSP: support strict mode β€” no unsafe-inline; use class-based styling & event handlers.
  • SRI: entry + wasm + CSS must include integrity.
  • Sandbox: no untrusted HTML injection; sanitize any dynamic content.
  • Endpoint allowlist: only call BFF routes approved in permissions.endpoints.
  • No localStorage secrets; use shell-managed auth via cookies (BFF).

πŸ“¦ Distribution

  • Publish NuGet (developer helper APIs, DI & bridge contracts).
  • Publish CDN bundle with manifest for shell runtime loading.
  • Optional NPM wrapper for non-.NET hosts (future).

βœ… Guarantees

  • The MFE template compiles to a reusable Custom Element with shell-awareness.
  • Loading is manifest-driven with SRI and capability gating.
  • Theme, culture, and density sync seamlessly from the shell.
  • CI emits NuGet + CDN artifacts and a validated manifest ready for portal composition.

πŸ“‘ Microfrontend Runtime Contracts

🎯 Purpose

Define the formal contracts that allow the shell to discover, validate, authorize, load, and communicate with Microfrontends (MFEs) at runtime. This includes the manifest schema, the event/command bus protocol, and a capability allowlist that enforces least privilege.

πŸ’‘ If it’s not in the contract, it doesn’t exist at runtime. Contracts β‰  docs β€” they’re the rules the shell enforces.


πŸ“œ Manifest Schema (microfrontend.json)

The manifest is the source of truth for MFE identity, assets, capabilities, and compatibility.

{
  "$schema": "https://catalog.connectsoft.dev/schemas/mfe/1-0-0.json",
  "name": "cs.billing",                    // unique, kebab/camel allowed
  "displayName": "Billing",
  "version": "1.2.3",
  "element": "cs-mfe-billing",             // Custom Element tag to render
  "entry": {
    "script": "https://cdn.example/cs.billing/1.2.3/entry.js",
    "wasm":   "https://cdn.example/cs.billing/1.2.3/app.wasm",
    "assets": [
      "https://cdn.example/cs.billing/1.2.3/styles.css"
    ],
    "integrity": {
      "entry.js": "sha384-…",
      "app.wasm": "sha384-…",
      "styles.css": "sha384-…"
    }
  },
  "i18n": ["en","ru","he"],                // supported UI languages
  "theming": ["light","dark"],             // visual modes MFE supports
  "density": ["compact","cozy","comfortable"],
  "capabilities": ["notify","openBlade"],  // requested shell features
  "permissions": {
    "endpoints": ["bff:/billing/*"]        // BFF allowlist (shell enforces)
  },
  "minShellVersion": "1.0.0",
  "telemetry": {
    "activitySource": "CS.MFE.Billing",
    "metricsPrefix": "cs_mfe_billing_"
  },
  "health": {
    "statusEndpoint": "https://cdn.example/cs.billing/1.2.3/health.json"
  }
}

Rules

  • version uses SemVer; shell compares against minShellVersion.
  • All entry.* assets must include SRI (integrity) and be served with CORS headers compatible with CSP.
  • permissions.endpoints is deny-by-default: shell proxies only allowlisted routes.
  • Manifest must be immutable post-release; new versions publish a new manifest URL.

πŸ“Œ Manifests are machine-validated; human-readable fields (like displayName) must not affect behavior.


🧩 Schema Fragments (JSON Schema)

Identity & Compatibility

{
  "type": "object",
  "required": ["name","version","element","entry","capabilities","minShellVersion"],
  "properties": {
    "name":   { "type":"string", "pattern":"^[a-z0-9_.-]+$" },
    "version":{ "type":"string", "pattern":"^\\d+\\.\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$" },
    "element":{ "type":"string", "pattern":"^[a-z][a-z0-9-]*$" },
    "minShellVersion": { "type":"string" }
  }
}

Entry Assets & Integrity

{
  "entry": {
    "type": "object",
    "required": ["script", "wasm", "assets", "integrity"],
    "properties": {
      "script": { "type":"string", "format":"uri" },
      "wasm":   { "type":"string", "format":"uri" },
      "assets": { "type":"array", "items": { "type":"string", "format":"uri" } },
      "integrity": {
        "type":"object",
        "additionalProperties": { "type":"string", "pattern":"^sha(256|384|512)-" }
      }
    }
  }
}

Capabilities & Permissions

{
  "capabilities": {
    "type":"array",
    "items": { "enum": ["notify","openBlade","navigate","activityLog","clipboard"] },
    "uniqueItems": true
  },
  "permissions": {
    "type":"object",
    "properties": {
      "endpoints": {
        "type":"array",
        "items": { "type":"string", "pattern":"^(bff:/.+)$" },
        "uniqueItems": true
      }
    },
    "additionalProperties": false
  }
}

🚌 Event / Command Bus

Bidirectional Shell ⇄ MFE communication uses typed events and commands. Transport is abstract; implementations may use direct host API, DOM CustomEvents, or postMessage. The protocol (names, payloads) is stable.

Event Names (Shell β†’ MFE)

  • Shell.ThemeChanged β†’ { theme: "light|dark", density: "compact|cozy|comfortable" }
  • Shell.CultureChanged β†’ { culture: "en|ru|he" }
  • Shell.TenantChanged β†’ { tenantId: "…" , edition: "Pro|Enterprise" }
  • Shell.FeatureFlagsUpdated β†’ { flags: Record<string, boolean> }

Command Names (MFE β†’ Shell)

  • Mfe.Notify β†’ { message: string, severity: "info|success|warning|danger" } (requires notify)
  • Mfe.OpenBlade β†’ { bladeId: string, args?: object } (requires openBlade)
  • Mfe.Navigate β†’ { path: string } (requires navigate)
  • Mfe.LogActivity β†’ { category: string, details?: object } (requires activityLog)

πŸ’‘ Capabilities gate commands: if openBlade isn’t in the manifest, the shell rejects the command.

DOM Event Example

// within custom element instance
this.dispatchEvent(new CustomEvent("Mfe.Notify", {
  bubbles: true,
  detail: { message: "Saved", severity: "success" }
}));

Host API Example

// shell injects a host object onto the CE instance
ce.hostApi.openBlade({ bladeId: "billing.invoice", args: { id: 123 } });

πŸ›‘οΈ Capability Allowlist (Least Privilege)

Principles

  • Capabilities are explicit opt-ins declared in the manifest.
  • Shell may deny at runtime via policy (tenant/edition/role).
  • New capabilities are versioned; old shells ignore unknown capabilities.

Standard Set (v1)

  • notify β€” raise a shell notification.
  • openBlade β€” open a specific blade in the shell.
  • navigate β€” request a route change within the shell.
  • activityLog β€” append to the shell’s activity stream.
  • clipboard β€” request text copy via a shell-mediated API (CSP-safe).

Policy Evaluation

  • Manifest β†’ capability request
  • Shell policy (tenant/edition/role) β†’ allow/deny
  • Command invocation β†’ checked against effective capabilities (request ∩ policy)

πŸ“Œ Deny-by-default. Capability elevation requires manifest change and policy approval.


🧰 Shell Registry & Loading Flow

  1. Discovery
    • Shell fetches manifest (signed/immutable URL) from its allowlist registry.
  2. Validation
    • JSON Schema validate; SRI checks; compatibility (minShellVersion).
  3. Policy
    • Capabilities intersect with tenant/edition/role policies.
  4. Load
    • Inject entry.script; verify SRI; instantiate element tag.
  5. Wire
    • Provide host API (or subscribe to CE events); send initial Shell.* events.

Failure Modes (and UI)

  • Invalid schema β†’ Block + log (activity + telemetry).
  • SRI mismatch β†’ Block + security alert.
  • Incompatible version β†’ Offer downgrade or skip with explanation.
  • Denied capability β†’ Load without that command; return structured error if invoked.

πŸ§ͺ Validation & QA Anchors

  • Schema validation (CI + shell runtime).
  • SRI verification for each asset; fail build if missing.
  • Policy simulation: run matrix tests (capabilities Γ— tenant/edition/role).
  • Backward-compat suite: ensure older shells ignore unknown fields gracefully.
  • Contract tests: emit each Mfe.* command and assert shell responses (allow/deny).

πŸ” Security Considerations

  • CSP-compatible loading (no inline JS/CSS).
  • No eval or dynamic script URLs; only registry-approved CDN origins.
  • BFF-only network: MFEs never call third-party APIs directly; the shell proxies.
  • Data sanitization: notifications & logs are HTML-escaped by the shell.
  • Rate-limits on chatty commands (e.g., Notify, ActivityLog).

πŸ” Versioning & Evolution

  • Schema version is part of $schema URL (e.g., 1-0-0).
  • Additive-first: new optional fields/capabilities are backward compatible.
  • Breaking changes β†’ new major schema; shell may support multiple concurrently.
  • Capability set evolves with RFCs; each new item carries security guidance and policy knobs.

βœ… Guarantees

  • MFEs are self-describing via a strict manifest.
  • Shell enforces capability least privilege and policy gates.
  • Communication uses a stable, typed bus with clear allow/deny outcomes.
  • Loading is secure (CSP/SRI), validated, and backward compatible across versions.

πŸš€ Advanced Microfrontend Features

🎯 Purpose

Elevate the Microfrontend Library with secure API access (BFF/YARP), runtime feature flags, and WASM-safe observability. This turns MFEs into production-grade units that integrate cleanly with enterprise shells and backends.

πŸ’‘ The MFE never talks to raw APIs β€” it talks to the BFF. Observability is on by default, not bolted on.


πŸ”Œ BFF/YARP API Client Boilerplate

Goals

  • Ensure zero-token handling in the browser (cookies via BFF).
  • Provide a typed HttpClient preconfigured for relative /bff/* routes.
  • Propagate trace context and tenant/edition headers.

Boilerplate

// Startup.cs (WASM)
builder.Services.AddScoped(sp =>
{
  var http = new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) };
  http.DefaultRequestHeaders.Add("X-CS-Tenant",  ShellContext.TenantId ?? "default");
  http.DefaultRequestHeaders.Add("X-CS-Edition", ShellContext.Edition ?? "Pro");
  return http;
});

// Typed client
public sealed class BillingApi
{
  private readonly HttpClient _http;
  public BillingApi(HttpClient http) => _http = http;

  public async Task<InvoiceDto[]> ListAsync(CancellationToken ct = default)
    => await _http.GetFromJsonAsync<InvoiceDto[]>("/bff/billing/invoices", ct) 
       ?? Array.Empty<InvoiceDto>();

  public async Task PayAsync(Guid id, CancellationToken ct = default)
    => (await _http.PostAsync($"/bff/billing/pay/{id}", content: null, ct)).EnsureSuccessStatusCode();
}

Guidelines

  • All network calls go through /bff/*; server enforces auth, RBAC, and downstream service access.
  • No token in JS; auth cookies managed server-side.
  • Timeouts, retries, and circuit-breakers are handled on the server, not the browser.

πŸ“Œ If a call needs third-party access, the BFF integrates it. MFEs remain sandboxed.


πŸŽ›οΈ Feature Flag Hooks

Goals

  • Runtime flag evaluation with sticky treatments (per tenant/user/edition).
  • Consistent interface across MFEs and shell.

Contract & Provider

public interface IFeatureFlags
{
  bool IsEnabled(string key);
  event EventHandler<FlagsChangedEventArgs>? Changed;
}

public sealed record FlagsChangedEventArgs(IReadOnlyDictionary<string, bool> Flags);

public static class FeatureFlagKeys
{
  public const string Billing_BetaBlade = "billing.betaBlade";
  public const string Billing_NewGrid   = "billing.newGrid";
}

Bootstrap & Updates

  • Initial values can come from the manifest defaults or a shell-provided snapshot.
  • Shell broadcasts updates via Shell.FeatureFlagsUpdated β†’ provider updates β†’ raises Changed.

Usage (component)

@inject IFeatureFlags Flags

@if (Flags.IsEnabled(FeatureFlagKeys.Billing_BetaBlade))
{
  <BetaBlade />
}
else
{
  <StableBlade />
}

Sticky Behavior

  • Provider stores sticky decisions in memory (session) or via BFF cookie keyed by tenant/user.
  • Respect edition overrides (Enterprise vs Pro) before local toggles.

πŸ“ˆ WASM Observability

Goals

  • Provide Activity/trace correlation from MFE β†’ BFF β†’ downstream services.
  • Emit structured logs and user action events.
  • Keep it WASM-safe (avoid APIs not available in browser).

Setup

public static class Telemetry
{
  public static readonly ActivitySource Source = new("CS.MFE.Sample");

  public static IDisposable StartUiActivity(string name, params (string, object)[] tags)
  {
    var activity = Source.StartActivity(name, ActivityKind.Internal);
    if (activity != null)
      foreach (var (k, v) in tags) activity.SetTag(k, v);
    return activity ?? Disposable.Nop;
  }
}

Usage

using var _ = Telemetry.StartUiActivity("Invoice.Pay", ("invoice.id", id));
await _api.PayAsync(id, ct);
_logger.LogInformation("Invoice payment requested {InvoiceId}", id);

Trace Context

  • BFF adds/propagates W3C traceparent/tracestate across services.
  • Browser β†’ BFF correlation achieved via request headers (added by fetch/HttpClient automatically when same-origin).

Metrics (lightweight)

  • Use counters for user actions (cs_mfe_clicks_total, cs_mfe_api_errors_total).
  • Prefer in-memory accumulators; exporting is done server-side (BFF aggregates).

Error Reporting

  • Wrap UI actions; send minimal error events to BFF (PII scrubbed).
  • Avoid sending stack traces to analytics; log IDs & correlation only.

πŸ’‘ The browser emits enough to correlate; the server does the heavy lifting.


πŸ” Security & Privacy

  • No secrets or tokens stored client-side.
  • Enforce CSP; avoid inline handlers; ship scripts with SRI.
  • PII minimization: logs contain IDs and categories, never raw user content.
  • Feature flags must not leak entitlements via DOM attributes or global vars.

πŸ§ͺ Testing & QA Anchors

  • API: contract tests against a mock BFF route set (/bff/billing/*).
  • Flags: matrix tests (flag on/off Γ— edition) for rendering paths.
  • Telemetry: assert that user actions emit activities and that correlation headers are present.
  • Resilience: simulate BFF failures/timeouts; ensure UI fallbacks and retries are sane.

πŸ“¦ Distribution & Config

  • Typed API clients live in the MFE package; endpoints configurable via element attribute (data-endpoint) or shell DI.
  • Feature flag provider is injected; default is in-memory with shell sync, advanced providers can wrap ECS/AppConfig.
  • Telemetry namespaces (ActivitySource, metric prefixes) are declared in manifest to aid server aggregation.

βœ… Guarantees

  • MFEs access data only through BFF, with correlation and policy enforcement.
  • Feature flags are runtime-tunable, edition-aware, and sticky per context.
  • Observability is WASM-safe, enabling end-to-end traces without browser-only hacks.
  • Security and privacy constraints are non-negotiable defaults.

πŸ›οΈ Application Shell Template

🎯 Purpose

Provide a Blazor Web App shell that acts as the portal spine: it owns layout (left rail + top bar + blade/portal canvas), theme/density state, and runtime composition of MFEs loaded as Custom Elements. The shell is the single point of UX truth: navigation, context (tenant/edition), and cross-cutting concerns (theming, localization, notifications).

πŸ’‘ MFEs bring features; the shell brings order. It’s the conductor of the orchestra.


🧱 Template Output

  • Template ID: connectsoft.blazor.appshell
  • Project Name: ConnectSoft.Blazor.AppShell
  • Artifacts: NuGet package (scaffold + helpers), runnable project template, sample portal with a mock MFE registry

πŸ“ Project Layout (generated)

src/
  ConnectSoft.Blazor.AppShell/
    App.razor
    Program.cs
    Pages/
      _Host.cshtml                 # if using Blazor Web App w/ SSR
      Home.razor
    Layout/
      PortalLayout.razor           # left rail + top bar + blade host
      LeftNav.razor
      TopBar.razor
      BladeHost.razor
    Components/
      NotificationCenter.razor
      ThemeSwitch.razor
      DensitySwitch.razor
      MfeHost.razor                # CE wrapper & lifecycle
    State/
      ShellState.cs                # theme, density, tenant, edition
      ShellReducers.cs             # (optional) if using Fluxor
      ShellEvents.cs               # typed events for bus
    Registry/
      ManifestRegistry.cs          # allowlist & discovery
      MfeDescriptor.cs             # element, permissions, version
    Services/
      ShellBridgeHost.cs           # event/command bus (host side)
      ThemeService.cs
      LocalizationService.cs
      NotificationService.cs
    wwwroot/
      css/site.css
      js/mfe-host.js               # CE loader, SRI verify, events
    ConnectSoft.Blazor.AppShell.csproj
template/
  .template.config/template.json

🧭 Layout System (left rail + top bar + blade canvas)

Left Rail

  • Groups: Favorites, Workspace, Admin (configurable)
  • Items: icon, title, route, optional blade target (bladeId)
  • Collapsible, keyboard accessible (Arrow navigation, Home/End)

Top Bar

  • Tenant selector, Search/Command palette entry point
  • Theme & density toggles, User menu
  • Notification bell with unread indicator

Blade/Portal Canvas

  • Multi-pane blades with sizes: narrow|normal|wide|full
  • Stack & focus management; ESC closes topmost
  • URL-bound: /blades/{bladeId}?args=… or state-bound with deep link support
@* PortalLayout.razor *@
<div class="h-screen grid grid-cols-[auto_1fr] grid-rows-[auto_1fr]">
  <LeftNav class="col-start-1 row-span-2" />
  <TopBar class="col-start-2 row-start-1" />
  <BladeHost class="col-start-2 row-start-2" />
</div>

πŸ“Œ If the shell can’t open and close a blade via keyboard only, it’s not a blade.


🎨 Theme & Density State

  • State lives in ShellState with two-way binding to UI and persistent storage (cookie/localStorage per tenant).
  • Attributes applied to root for tokens consumption:
    • data-theme="light|dark"
    • data-density="compact|cozy|comfortable"
    • Optional data-a11y="high-contrast"
public sealed class ShellState
{
  public string Theme { get; private set; } = "light";
  public string Density { get; private set; } = "cozy";
  public string? TenantId { get; private set; }
  public string Edition { get; private set; } = "Pro";

  public event Action? Changed;

  public void SetTheme(string theme) { Theme = theme; Changed?.Invoke(); }
  public void SetDensity(string density) { Density = density; Changed?.Invoke(); }
  public void SetTenant(string? id, string edition) { TenantId = id; Edition = edition; Changed?.Invoke(); }
}

Propagation

  • ThemeService updates DOM attributes & persists preference.
  • ShellBridgeHost broadcasts Shell.ThemeChanged / Shell.CultureChanged to MFEs.

🧩 MFE Hosting & Composition

Registry & Allowlist

  • ManifestRegistry stores approved manifests (origin allowlist, capability policy).
  • Supports local dev (file manifests) and CDN (signed URLs).

MfeHost.razor

  • Receives MfeDescriptor (from route, command, or nav)
  • Loads manifest β†’ verifies SRI β†’ injects script β†’ creates CE element
  • Passes context as attributes: data-tenant, data-edition, data-theme, lang, data-density, optional data-endpoint
  • Wires host API on CE instance or subscribes to DOM events
@code {
  [Parameter] public MfeDescriptor Mfe { get; set; } = default!;
  protected override async Task OnAfterRenderAsync(bool firstRender)
    => await Js.InvokeVoidAsync("csMfeHost.mount", elementRef, Mfe);
}

Host JS (excerpt)

export async function mount(hostEl, mfe) {
  await loadWithSri(mfe.entry.script, mfe.entry.integrity["entry.js"]);
  const ce = document.createElement(mfe.element);
  ce.setAttribute("data-theme", getTheme());
  ce.setAttribute("data-density", getDensity());
  ce.setAttribute("lang", getCulture());
  hostEl.appendChild(ce);
  wireHostApi(ce); // openBlade, notify, navigate…
}

  • Router: standard Blazor routing (@page) for pages and blade routes.
  • Blade Navigation:
    • Programmatic: ShellBridgeHost.OpenBlade("billing.invoice", new { id = 42 })
    • Declarative from nav items (left rail)
  • Deep Links:
    • URL encodes blade id & args; shell reconstructs blade stack on refresh.

πŸ”” Notifications & Activity

  • NotificationService stores transient toasts & persistent activity items.
  • MFEs invoke Mfe.Notify / Mfe.LogActivity β†’ capability & policy checked β†’ show & persist.

🌐 Localization

  • LocalizationService manages CultureInfo.CurrentUICulture and sends Shell.CultureChanged events.
  • Language selector in TopBar; persisted per tenant.

β™Ώ Accessibility

  • Every interactive item in left rail & top bar is reachable via Tab and arrow keys.
  • Blades expose role="dialog" / aria-modal="true" when modal; non-modal panes still keep focus order predictable.
  • Visible focus ring driven by tokens; high-contrast mode via data-a11y.

πŸ§ͺ Testing & QA Anchors

  • Playwright: nav keyboard traversal, blade open/close, focus trap, deep-link reload.
  • a11y: axe/Pa11y on key routes; fail on AA contrast/role issues.
  • MFE smoke: host mounts a sample MFE from local manifest; verifies event roundtrip.
  • Regression: snapshots for layout structure (left rail, top bar, blade host) with minimal fragility.

βš™οΈ Extension Points

  • Command Palette entry (Ctrl/Cmd+K) β†’ later cycle can plug providers (routes, MFEs).
  • Search provider interface for resource lookup.
  • Edition/Feature Flag provider (ECS/AppConfig) to filter nav items & blades dynamically.

πŸ” Security & Compliance

  • CSP strict defaults; inline scripting disabled; nonces/hashes for shell assets.
  • SRI enforced for MFE scripts; only allowlisted CDN origins.
  • No direct external APIs from shell; all calls via BFF (configured next cycles).
  • XSS hygiene: notifications/activity text escaped; manifest fields sanitized.

πŸ“¦ Distribution

  • Ships as:
    • Template: dotnet new cs-blazor-appshell runnable portal
    • NuGet helper: shared services (ThemeService, ShellBridgeHost, ManifestRegistry), ready to reuse across shells
  • Includes a SamplePortal wiring a sample MFE registry (local manifests) for immediate run.

πŸ’‘ If the sample portal can’t compose a demo MFE in 60 seconds from dotnet new, the shell template is not done.


βœ… Guarantees

  • A portal-grade Blazor Web App shell with left rail, top bar, and blade canvas.
  • Theme/density/localization state owned by the shell and propagated to MFEs.
  • Secure, policy-enforced MFE hosting with SRI/CSP and a validated registry.
  • Keyboard-accessible, token-driven layout β€” predictable and extensible from day one.

πŸ” Authentication & BFF Integration

🎯 Purpose

Establish OIDC cookie authentication for the shell, and a backend-for-frontend (BFF) that proxies all browser API calls through YARP (or the ConnectSoft Gateway). The browser never handles tokens; the server maintains a secure session cookie, validates tokens, and enforces tenant/edition/RBAC before calling downstream services.

πŸ’‘ Rule #1: MFEs and the shell never touch access tokens. The BFF does the heavy lifting.


🧭 Architecture

flowchart LR
  Browser[Browser - Shell + MFEs] -- cookie --> BFF[BFF - AppShell Server]
  BFF -- mTLS/JWT --> Gateway[ConnectSoft Gateway / YARP Routes]
  Gateway --> SvcA[Billing API]
  Gateway --> SvcB[Catalog API]
  IdP[(OIDC Provider)]
  Browser <--> IdP
  BFF <--> IdP
Hold "Alt" / "Option" to enable pan & zoom
  • OIDC: Authorization Code + PKCE β†’ cookie session in shell (server).
  • BFF: Same-origin /bff/* routes; adds correlation, tenant, edition, user claims; strips cookies for downstream hops; caches tokens server-side.
  • Gateway/YARP: routes /bff/billing/* β†’ https://billing.api/..., /bff/catalog/* β†’ https://catalog.api/....

βš™οΈ Shell OIDC Configuration

Key choices

  • Cookie auth (HttpOnly, Secure, SameSite=Lax).
  • Sliding expiration with short access token lifetime, long refresh (server-only).
  • Stateful session (server cache) or distributed (Redis) for scale-out.

Program.cs (excerpt)

var builder = WebApplication.CreateBuilder(args);

builder.Services
  .AddAuthentication(options =>
  {
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
  })
  .AddCookie(options =>
  {
    options.Cookie.Name = "__CS.Auth";
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Lax;
    options.SlidingExpiration = true;
  })
  .AddOpenIdConnect(options =>
  {
    options.Authority = builder.Configuration["Auth:Authority"];
    options.ClientId = builder.Configuration["Auth:ClientId"];
    options.ClientSecret = builder.Configuration["Auth:ClientSecret"]; // if confidential
    options.ResponseType = "code";
    options.UsePkce = true;
    options.SaveTokens = false; // tokens stay server-side only
    options.GetClaimsFromUserInfoEndpoint = true;

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");
    options.Scope.Add("cs.api"); // example API scope

    options.TokenValidationParameters = new TokenValidationParameters
    {
      NameClaimType = "name",
      RoleClaimType = "role",
      ValidateIssuer = true,
      ValidateAudience = true
    };
  });

builder.Services.AddAuthorization(options =>
{
  options.AddPolicy("TenantScoped", policy => policy.RequireClaim("tenant_id"));
  options.AddPolicy("EditionPro", policy => policy.RequireAssertion(ctx =>
    ctx.User.HasClaim("edition", "Pro") || ctx.User.HasClaim("edition", "Enterprise")));
});

Login/Logout endpoints

app.MapGet("/login", () => Results.Challenge(new()
{
  RedirectUri = "/"
}, new[] { OpenIdConnectDefaults.AuthenticationScheme }));

app.MapPost("/logout", async (HttpContext ctx) =>
{
  await ctx.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
  await ctx.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, new()
  {
    RedirectUri = "/signed-out"
  });
});

πŸ“Œ Silent renew is handled via OIDC refresh on the server; the browser only sees a cookie.


πŸŒ‰ BFF & YARP Proxy

Goals

  • Single same-origin entrypoint /bff/*.
  • Inject tenant/edition/trace headers; enforce RBAC/ABAC.
  • Remove browser cookies for outbound calls; use server-held tokens or mTLS.

YARP configuration (appsettings.json)

{
  "ReverseProxy": {
    "Routes": {
      "billing": {
        "ClusterId": "billing",
        "Match": { "Path": "/bff/billing/{**catch-all}" },
        "Transforms": [
          { "PathRemovePrefix": "/bff" },
          { "RequestHeaderOriginalHost": "true" }
        ]
      },
      "catalog": {
        "ClusterId": "catalog",
        "Match": { "Path": "/bff/catalog/{**catch-all}" },
        "Transforms": [
          { "PathRemovePrefix": "/bff" }
        ]
      }
    },
    "Clusters": {
      "billing": { "Destinations": { "d1": { "Address": "https://billing.api/" } } },
      "catalog": { "Destinations": { "d1": { "Address": "https://catalog.api/" } } }
    }
  }
}

Runtime enrichment

app.Use(async (ctx, next) =>
{
  if (ctx.Request.Path.StartsWithSegments("/bff"))
  {
    var user = ctx.User;
    if (!user.Identity?.IsAuthenticated ?? true)
      throw new UnauthorizedAccessException();

    // Attach tenant/edition/trace headers
    ctx.Request.Headers["X-CS-Tenant"] = user.FindFirst("tenant_id")?.Value ?? "default";
    ctx.Request.Headers["X-CS-Edition"] = user.FindFirst("edition")?.Value ?? "Pro";
    ctx.Request.Headers["x-correlation-id"] = Activity.Current?.TraceId.ToString() ?? Guid.NewGuid().ToString();
  }
  await next();
});

Token handling

  • Downstream calls use server-side access tokens acquired via OIDC on behalf of user (or client credentials for service calls).
  • Tokens stored in server cache (protected), not emitted to browser.
  • For ConnectSoft Gateway, use mTLS or JWT exchange at the edge; map user claims to internal roles/scopes.

πŸ’‘ No Authorization headers from the browser. The BFF crafts them per request.


🧩 Tenant, Edition & RBAC

  • Claims issued by IdP: tenant_id, edition, role/permissions.
  • Policies:
    • Router guards for shell pages/blades (e.g., Admin only).
    • BFF guards for route families (e.g., /bff/billing/** requires role: billing.read).
  • Edition gating:
    • Shell hides nav items/blades if user’s edition < required.
    • BFF rejects requests that attempt unauthorized editions or tenants.

πŸ›‘οΈ Security Posture

  • Cookies: HttpOnly, Secure, SameSite=Lax, short lifetime, sliding expiration.
  • CSRF: anti-forgery tokens on unsafe methods (POST/PUT/PATCH/DELETE) for /bff/**.
  • CSP: strict; MFEs loaded with SRI; only allowlisted CDNs; forbid unsafe-inline.
  • PII Minimization: logs carry IDs/codes, not raw user content.
  • Rate limiting: apply per-user limits on chatty BFF routes.
  • Header Hygiene: strip inbound Authorization / sensitive headers before proxying.

🌐 MFE Consumption of BFF

MFEs use relative routes, the shell injects base:

// In MFE bootstrap
builder.Services.AddScoped(sp =>
{
  var http = new HttpClient { BaseAddress = new Uri("/", UriKind.Relative) };
  http.DefaultRequestHeaders.Add("X-CS-Tenant",  ShellContext.TenantId ?? "default");
  http.DefaultRequestHeaders.Add("X-CS-Edition", ShellContext.Edition ?? "Pro");
  return http;
});

All calls like GET /bff/billing/invoices remain same-origin β†’ cookie session applies automatically.


πŸ”„ Session & Token Refresh

  • Server refreshes tokens using refresh token or client credentials β†’ rotates access tokens before expiry.
  • If refresh fails (revoked/expired), the next /bff/* call returns 401; shell redirects to /login.
  • Blacklist users/tenants mid-session via server-side session eviction (cache removal).

πŸ§ͺ Testing & QA Anchors

  • OIDC stub in CI for deterministic auth flows.
  • Cookie semantics: assert HttpOnly/Secure/SameSite flags.
  • CSRF tests: POST without token β†’ 400; with valid token β†’ 200.
  • Proxy policy: unauthorized roles/editions/tenants β†’ 403.
  • Header tests: no Authorization from browser; X-CS-* injected server-side.
  • Trace correlation: /bff/* adds traceparent; downstream logs correlate to shell request.

πŸ“¦ Configuration & Secrets

appsettings.json (per environment) holds Authority, ClientId, Scopes; secrets via KeyVault/KeyChain. YARP routes and ConnectSoft Gateway endpoints configured by environment. Enable distributed cache for sessions in multi-instance deployments.


βœ… Guarantees

  • The shell authenticates via OIDC and maintains a server cookie session.
  • All browser-originated API calls go through a BFF/YARP layer (or ConnectSoft Gateway).
  • Tokens are never exposed to the browser; headers are enriched server-side.
  • Tenant/edition/RBAC are enforced centrally; CSRF/CSP/SRI hardening is built in.

πŸ“‘ Real-Time Events & SignalR

🎯 Purpose

Establish a reliable, secure, and scalable real-time layer for the portal:

  • Notifications (toasts, alerts)
  • Activity feed (auditable stream)
  • Bidirectional Shell ↔ MFE communication (command/event bus with live updates)

πŸ’‘ If the experience needs a refresh button, we’re not done. Real-time is a first-class UX channel.


🧭 Architecture

flowchart LR
  subgraph Browser
    Shell[App Shell] <--> MFE1[Custom Element A]
    Shell <--> MFE2[Custom Element B]
  end
  Shell <--> HubSignalR[(SignalR Hub)]
  HubSignalR --> BFF[BFF Policies]
  BFF --> SvcA[Domain Services]
  BFF --> Bus[Event Bus/Outbox]
Hold "Alt" / "Option" to enable pan & zoom
  • SignalR Hub: single multiplexed connection per user/session.
  • Shell owns the SignalR client and fans-in/out events to MFEs via the bus.
  • BFF authorizes hub connections, enriches context (tenant, edition, roles), and optionally bridges backend events (via outbox/bus).

πŸ”Œ Contracts (Shell Bus)

Inbound (Server β†’ Shell)

  • NotificationPushed β†’ { id, ts, severity, title, message, tags? }
  • ActivityAppended β†’ { id, ts, category, title, details }
  • FlagsUpdated β†’ { flags: Record<string, boolean> }
  • ResourceChanged β†’ { kind, id, changeType, data? }

Outbound (Shell β†’ MFEs) via typed events

  • Shell.ThemeChanged, Shell.CultureChanged, Shell.TenantChanged, Shell.FeatureFlagsUpdated (already defined)
  • Shell.Notification (selective forward)
  • Shell.Activity (selective forward)

Inbound (MFEs β†’ Shell)

  • Mfe.Notify β†’ request to raise notification
  • Mfe.LogActivity β†’ request to append activity record

πŸ“Œ MFEs never connect to SignalR directly. The Shell is the broker.


πŸ›°οΈ SignalR Hub Design

Endpoints

  • /hubs/shell (authenticated, cookie session)
  • Groups by tenant, user, and capabilities:
    • tenant:{tenantId}
    • user:{userId}
    • cap:{notify}, cap:{activityLog} (derived from effective policy)

Hub methods (server β†’ client)

public interface IShellClient
{
  Task NotificationPushed(NotificationDto dto);
  Task ActivityAppended(ActivityDto dto);
  Task FlagsUpdated(Dictionary<string, bool> flags);
  Task ResourceChanged(ResourceChangedDto dto);
}

Hub invocations (client β†’ server) β€” minimal; primarily admin/ops or ACKs when needed.


πŸ”” Notifications

Schema

public sealed record NotificationDto(
  string Id,
  DateTimeOffset Timestamp,
  string Severity,   // info|success|warning|danger
  string Title,
  string Message,
  string[]? Tags = null,
  string? CorrelationId = null);

Flow

  1. Backend emits domain event β†’ BFF translates to NotificationDto.
  2. Hub broadcasts to user:{id} and optionally tenant:{tenantId}.
  3. Shell receives and:
    • Stores in NotificationCenter (persist for session)
    • Optionally toasts via Adapter (IUiToastAdapter)
    • Fan-out to MFEs that requested notify capability (policy-filtered)

πŸ’‘ Notifications are short-lived UX artifacts; Activity is the durable audit trail.


πŸ““ Activity Feed (Durable)

Schema

public sealed record ActivityDto(
  string Id,
  DateTimeOffset Timestamp,
  string Category,   // e.g., "billing.invoice"
  string Title,
  object? Details = null,
  string? Actor = null,
  string? CorrelationId = null);

Flow

  • Domain services write to outbox β†’ ingested by BFF β†’ ActivityAppended on hub.
  • Shell persists a rolling window in memory (for UI) and loads history via BFF on demand.
  • MFEs may request an append via Mfe.LogActivity (capability & policy enforced).

πŸ” Shell Event Fan-Out

Inbound from Hub

  • Normalize payload β†’ update Shell state/services.
  • Selective forwarding to MFEs:
    • Forward only if the MFE declared interest (capability + subscription).
    • Forward via host API on the CE instance or DOM CustomEvent.
// Shell side: forward a notification to interested MFEs
for (const ce of hostedMFEs.filter(x => x.capabilities.includes("notify"))) {
  ce.dispatchEvent(new CustomEvent("Shell.Notification", { detail: dto, bubbles: true }));
}

πŸ”’ Security & Policy

  • Auth: OIDC cookie; hub uses the same session as BFF.
  • Authorization: hub joins groups based on claims (tenant/edition/roles).
  • Capability gate: Shell forwards only events the MFE can handle (from manifest) and the policy allows.
  • PII Minimization: redact user content in notifications; activity details limited to IDs/codes.
  • Rate limiting: server throttles chatty channels; client debounces UI updates.
  • Replay protection: Id + Timestamp used to ignore duplicates client-side.

🧱 Reliability & Resilience

  • Reconnect strategy: exponential backoff; show passive banner (β€œReconnecting…”) in the shell.
  • Offline fallback: queue user actions; on reconnect, flush idempotently.
  • Backpressure: drop non-critical toasts when buffer exceeds threshold; never drop activity records (they reload from BFF).
  • Idempotency: use CorrelationId for de-duplication across hub + HTTP.

πŸ“ Observability

  • Emit ActivitySource spans: SignalR.Connect, SignalR.Receive.Notification, SignalR.Receive.Activity.
  • Metrics:
    • cs_shell_signalr_connections{tenant}
    • cs_shell_notifications_total{severity}
    • cs_shell_activity_total{category}
  • Logs (structured): user, tenant, capability, result (forwarded/blocked), latency.

πŸ§ͺ Testing & QA Anchors

  • Hub auth: unauthenticated β†’ 401; wrong tenant/edition β†’ no group membership.
  • Event round-trip: backend β†’ hub β†’ shell β†’ MFE; verify policy and capability gates.
  • Reconnect: simulate network loss; ensure queued notifications do not flood on resume.
  • A11y: toasts are non-blocking and dismissible; activity list is fully keyboard navigable.
  • Performance: stress test with N events/sec; verify UI remains responsive.

βš™οΈ Implementation Sketch

Server

builder.Services.AddSignalR().AddJsonProtocol();
app.MapHub<ShellHub>("/hubs/shell")
   .RequireAuthorization(); // cookie auth

public class ShellHub : Hub<IShellClient> { /* group joins in OnConnectedAsync */ }

Client (Shell)

var conn = new HubConnectionBuilder()
  .WithUrl("/hubs/shell", o => { o.AccessTokenProvider = null; }) // cookie auth
  .WithAutomaticReconnect()
  .Build();

conn.On<NotificationDto>("NotificationPushed", dto => _notifications.Add(dto));
conn.On<ActivityDto>("ActivityAppended", dto => _activity.Add(dto));
await conn.StartAsync();

βœ… Guarantees

  • Single, secured real-time channel per user, brokered by the shell.
  • Notifications and activity arrive live, are policy-filtered, and auditable.
  • MFEs communicate via a typed, capability-aware event bus β€” no direct hub access.
  • The system is observable, resilient, and accessible by default.

πŸŽ›οΈ Feature Flags & Edition Awareness

🎯 Purpose

Provide a unified feature control plane for the portal:

  • Tenant β†’ Edition β†’ Feature mapping,
  • Runtime flags (on/off, % rollouts, kill switches),
  • Synchronized evaluation across Shell and MFEs via a single provider abstraction.

πŸ’‘ Flags aren’t toggles; they’re policy. Edition awareness ensures the UI never exposes what the tenant isn’t entitled to.


🧭 Model & Hierarchy

Resolution order (highest precedence first):

  1. Tenant Overrides (support, hotfix, VIP tenants)
  2. Edition Policy (Free/Pro/Enterprise)
  3. Global Defaults (system-wide baseline)
flowchart TD
  Global[Global Defaults] --> Edition[Edition Policy]
  Edition --> Tenant[Tenant Overrides]
  Tenant --> Result[Effective Flags]
Hold "Alt" / "Option" to enable pan & zoom
  • Flags can be boolean, percentage rollouts, or targeted (by role/group).
  • Edition policy defines entitlements; flags can only narrow entitlements, not expand them beyond edition.

🧩 Contracts

Provider Abstraction (shared by Shell & MFEs):

public interface IFeatureFlags
{
  bool IsEnabled(string key);
  double GetRollout(string key); // 0..1, 1.0 == 100%
  event EventHandler<FlagsChangedEventArgs>? Changed;
  IReadOnlyDictionary<string, bool> Snapshot(); // for diagnostics
}

public sealed record FlagsChangedEventArgs(IReadOnlyDictionary<string, bool> Diff);

Key Registry (typed constants):

public static class FeatureKeys
{
  public static class Billing
  {
    public const string BetaBlade = "billing.betaBlade";
    public const string NewGrid = "billing.newGrid";
  }
  public static class Portal
  {
    public const string CommandPalette = "portal.commandPalette";
  }
}

Edition Map (declarative):

{
  "editions": {
    "Free":       { "portal.commandPalette": false, "billing.betaBlade": false, "billing.newGrid": false },
    "Pro":        { "portal.commandPalette": true,  "billing.betaBlade": false, "billing.newGrid": true  },
    "Enterprise": { "portal.commandPalette": true,  "billing.betaBlade": true,  "billing.newGrid": true  }
  }
}

βš™οΈ ECS/AppConfig Integration

Shell-side Provider (server):

  • Loads global defaults and edition entitlements from ECS/AppConfig at startup.
  • Applies tenant overrides (targeted rules, % rollouts) fetched per-tenant.
  • Exposes a typed snapshot to the UI and pushes updates via SignalR (Shell.FeatureFlagsUpdated).
builder.Services.AddSingleton<IFeatureFlags, ShellEcsFeatureFlags>();

MFE-side Provider (client/WASM):

  • Receives initial flags via:
    • Manifest defaults (optional), then
    • Shell bootstrap snapshot at mount time.
  • Subscribes to Shell.FeatureFlagsUpdated event via the host bridge and updates internal cache.
public sealed class MfeFeatureFlags : IFeatureFlags
{
  private readonly Dictionary<string,bool> _flags = new();
  public bool IsEnabled(string key) => _flags.TryGetValue(key, out var v) && v;
  public double GetRollout(string key) => IsEnabled(key) ? 1.0 : 0.0;
  public event EventHandler<FlagsChangedEventArgs>? Changed;

  public void ApplySnapshot(IReadOnlyDictionary<string,bool> snapshot)
  {
    var diff = snapshot.Where(kv => !_flags.ContainsKey(kv.Key) || _flags[kv.Key] != kv.Value)
                       .ToDictionary(kv => kv.Key, kv => kv.Value);
    foreach (var (k,v) in diff) _flags[k] = v;
    if (diff.Count > 0) Changed?.Invoke(this, new(diff));
  }
}

πŸ“Œ MFEs do not query ECS directly; the Shell is the source of truth for runtime policy.


πŸ” Edition Entitlements

Policy Rules

  • Entitlement-first: Determine whether a feature is eligible based on edition.
  • Flag-second: If eligible, flag can enable/disable or gradually roll out.
  • Shell applies UI filtering (hide nav, blades, commands) when not entitled.
  • BFF enforces the same policy on API routes (server authority).

UI Guard Example (Shell):

@if (Flags.IsEnabled(FeatureKeys.Portal.CommandPalette))
{
  <CommandPaletteButton />
}

API Guard Example (BFF):

app.MapGet("/bff/billing/invoices", [Authorize] async (HttpContext ctx, IFeatureFlags flags) =>
{
  if (!flags.IsEnabled(FeatureKeys.Billing.NewGrid))
    return Results.Forbid();
  // proceed
});

🎚️ Percentage Rollouts & Targeting

  • Rollouts computed server-side with stable hashing: hash(userId + key) < percentage.
  • Targeting rules: roles (billing.admin), groups, tenants, regions.
  • MFEs see only the effective boolean; rollout math is opaque to the client.
bool EvaluatePercentage(string userId, string key, double pct)
{
  var h = Murmur3.Hash(userId + key); // stable 0..1
  return h < pct;
}

πŸ”„ Runtime Updates

  • Shell listens for ECS/AppConfig changes β†’ recomputes effective flags per context β†’ pushes Shell.FeatureFlagsUpdated with diff only.
  • MFEs react to the Changed event and re-render affected regions.
// Host forwards updates into a custom element
ce.dispatchEvent(new CustomEvent("Shell.FeatureFlagsUpdated", { detail: flagsDiff }));

πŸ§ͺ Testing & QA Anchors

  • Matrix tests: (Edition Γ— Tenant Override Γ— Role) β†’ Expected flags.
  • UI hiding: Nav and blades should not render when not entitled; direct URL β†’ 403 by BFF.
  • Sticky behavior: Rollouts remain stable across sessions for the same user.
  • Hot toggles: Flip a flag in ECS; verify SignalR update β†’ Shell UI & MFE re-render.
  • Localization: Flag-driven banners/messages translated (verify RESX keys present).

πŸ“Š Observability

  • Audit: record who changed what flag, when, and scope (global/edition/tenant).
  • Metrics: cs_flags_updates_total, cs_flags_active{key,edition}.
  • Tracing: annotate spans with feature.flags for critical user actions.

🧱 Developer Experience

  • Feature Directory in docs: each key has purpose, owner, rollout plan, deprecation policy.
  • Playground in Sample Portal: switch edition (Free/Pro/Enterprise), simulate tenant override, see UI react.
  • Fail-closed guidance: always code secure defaults (feature off unless explicitly enabled).

πŸ’‘ If a feature must be always-on for an edition, encode it in entitlements, not just a flag.


βœ… Guarantees

  • Single source of truth for flags via Shell; MFEs consume effective state only.
  • Edition entitlements gate UI and APIs consistently (client + server).
  • Percentage rollouts and targeting are stable and auditable.
  • Real-time updates propagate safely, with minimal payloads and deterministic rendering.

πŸ“ˆ Observability & Diagnostics

🎯 Purpose

Deliver end-to-end visibility across Shell, MFEs, BFF, and backend services using OpenTelemetry for traces, logs, and metrics. Provide WASM-safe shims for the browser, standardized naming, and out-of-the-box Grafana/Azure Monitor dashboards so issues are fast to detect and easy to diagnose.

πŸ’‘ If you can’t see it, you can’t scale it. Observability is a product requirement, not an ops checkbox.


🧭 Scope & Pillars

  • Tracing: W3C trace-context from MFE β†’ Shell/BFF β†’ Services.
  • Logging: structured, PII-minimized logs with correlation IDs.
  • Metrics: RED/USE style signals for UX (client) and services (server).
  • Diagnostics: dev overlay, sampling toggles, incident bundles.

🧩 Naming Conventions

ActivitySource / Traces

  • Shell: CS.Shell (Shell.Route.Navigate, Shell.Blade.Open, SignalR.Receive.Notification)
  • MFE: CS.MFE.<Domain> (Mfe.UI.Action, Mfe.Api.Call)
  • BFF: CS.Bff (Bff.Proxy.Forward, Bff.Auth.Refresh)
  • Service: CS.Svc.<Name> (downstream standard)

Meters / Metrics

  • Prefix: cs_shell_*, cs_mfe_*, cs_bff_*, cs_svc_*
  • Examples:
    • cs_shell_blade_open_total{blade}
    • cs_mfe_api_errors_total{route}
    • cs_bff_proxy_latency_ms{cluster,route}
    • cs_shell_signalr_connections{tenant}

Logs

  • Logger categories map to components (e.g., ConnectSoft.Shell.Navigation, ConnectSoft.Mfe.Billing.Api).
  • Required fields: correlationId, tenantId, edition, userId (hashed/anonymized), capability.

πŸ“Œ Consistency wins: names are standardized, reviewed, and linted.


🧱 Server Instrumentation (Shell & BFF)

Traces & Metrics

builder.Services.AddOpenTelemetry()
  .WithTracing(t => t
    .AddAspNetCoreInstrumentation()
    .AddHttpClientInstrumentation()
    .AddGrpcClientInstrumentation()
    .AddSource("CS.Shell", "CS.Bff")
    .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("cs-shell"))
    .AddOtlpExporter())
  .WithMetrics(m => m
    .AddAspNetCoreInstrumentation()
    .AddHttpClientInstrumentation()
    .AddMeter("cs_shell_meter", "cs_bff_meter")
    .AddRuntimeInstrumentation()
    .AddProcessInstrumentation()
    .AddOtlpExporter());

Logs (structured)

builder.Logging.ClearProviders();
builder.Logging.AddJsonConsole(options => options.IncludeScopes = true);

Correlation Middleware

app.Use(async (ctx, next) =>
{
  var traceId = Activity.Current?.TraceId.ToString() ?? Guid.NewGuid().ToString("N");
  ctx.Items["CorrelationId"] = traceId;
  ctx.Response.Headers["x-correlation-id"] = traceId;
  using var activity = Observability.StartServerActivity("Http.Incoming", ("route", ctx.Request.Path));
  await next();
});

🌐 Client/WASM Instrumentation (MFE)

WASM-Safe Tracing

public static class MfeTelemetry
{
  public static readonly ActivitySource Source = new("CS.MFE.Billing");

  public static IDisposable Start(string name, params (string, object)[] tags)
  {
    var a = Source.StartActivity(name, ActivityKind.Internal);
    if (a is not null) foreach (var (k,v) in tags) a.SetTag(k, v);
    return a ?? new Noop();
  }

  private sealed class Noop : IDisposable { public void Dispose() { } }
}

Usage

using var _ = MfeTelemetry.Start("Mfe.Api.Call", ("route","/bff/billing/invoices"));
var data = await _api.ListAsync(ct);
_logger.LogInformation("Loaded invoices {@Count}", data.Length);

Client Metrics (in-memory)

  • Counters: cs_mfe_clicks_total{control}, cs_mfe_api_errors_total{route}
  • Histograms (optional in browser): cs_mfe_api_latency_ms{route}
  • Export strategy: push to BFF via lightweight /bff/telemetry batch (configurable, disabled by default).

⚠️ No heavy exporters in the browser. Keep memory footprint small; export via server when needed.


πŸ”Ž Diagnostics Overlay (Dev Only)

  • Toggle with ?diag=1 or palette command (β€œDiagnostics: Toggle”).
  • Panels:
    • Trace: current correlation ID, recent spans.
    • Flags: effective feature flags.
    • Theme: tokens resolved, contrast warnings.
    • Network: recent /bff/* calls with status & latency.
  • No PII; hidden in production builds.

πŸ“Š Grafana / Azure Monitor Templates

Dashboards (bundled JSON/ARM):

  • Portal Health: shell route latency, SignalR connections, error rate, top blades.
  • BFF Proxy: upstream latency per cluster/route, 4xx/5xx rates, retry/circuit stats.
  • MFE UX: API error totals by route, action frequencies, slowest UI actions.
  • Flags & Editions: active flag counts by edition, recent flips, rollout coverage.

Alerts

  • High cs_bff_proxy_latency_ms (p95 > SLO)
  • Spike in cs_shell_signalr_disconnects_total
  • Increase in cs_mfe_api_errors_total for a route
  • Error log rate per tenant crossing threshold

πŸ” Privacy & Security

  • PII minimization: hash userId, never log raw content.
  • Secrets: exporters use managed identity or key vault; no tokens client-side.
  • Sampling: head-based on server; tail-based allowed for rare errors; disable client sampling to keep code simple.
  • Redaction: explicit allowlist for log properties; drop unknown dynamic objects.

πŸ” Incident Bundle

  • On error spikes, server compiles an incident bundle:
    • time window spans, top logs, request samples (headers scrubbed), metric deltas.
  • Linked in the notification to on-call; downloadable from the ops portal.

πŸ§ͺ Testing & QA Anchors

  • Verify traceparent flows from browser β†’ BFF β†’ service (span linkage).
  • Ensure logs always include correlationId, tenantId, edition.
  • Synthetic load: open/close blades, fire notifications, API bursts β†’ dashboards update.
  • Toggle signal loss: SignalR reconnects, spans continue, no leaks.
  • Browser perf: telemetry disabled/enabled has < X ms overhead per action (budgeted).

πŸ“¦ Configuration

  • AppSettings: exporters (OTLP endpoint), sampling rates, PII policy.
  • Env Switches: OBS_ENABLE_CLIENT_EXPORT=false (default), OBS_SAMPLING_RATE=0.1 (10%).
  • Per-Tenant Overrides: raise/lower sampling during incidents.

βœ… Guarantees

  • Unified trace/log/metric story across Shell, MFEs, BFF, and services.
  • Browser instrumentation is WASM-safe, lightweight, and privacy-aware.
  • Teams get ready-to-use dashboards and alerts β€” not a blank observability canvas.
  • Correlation is first-class: every action is traceable end to end.

πŸ›‘οΈ Security & Compliance

🎯 Purpose

Embed security-first defaults and compliance-ready artifacts into all ConnectSoft Blazor templates. Every component library, microfrontend, and application shell must ship with defensive policies, signed packages, and verifiable manifests β€” making them safe for enterprise SaaS and regulatory audits.

πŸ’‘ Security is not optional glue; it is generated into every repo, enforced in every build, and validated at runtime.


🌍 Threat & Compliance Context

Common pitfalls in frontend SaaS systems:

  • ⚠️ Inline scripts/styles weaken Content Security Policy (CSP).
  • ⚠️ CDN assets without Subresource Integrity (SRI) risk tampering.
  • ⚠️ Overly broad CORS exposes APIs to hostile origins.
  • ⚠️ Unsigned packages erode supply chain trust.
  • ⚠️ Missing SBOMs make audits and CVE scans impossible.

Outcome: portals that fail enterprise reviews and invite security breaches.


πŸ”‘ Controls

1. Content Security Policy (CSP)

  • Default csp.config.json per app shell, generated with:
    • default-src 'self'
    • script-src 'self' https://cdn.connectsoft.net 'sha256-…'
    • style-src 'self' 'sha256-…'
    • object-src 'none'
    • frame-ancestors 'none'
  • Inline scripts forbidden; adapters use nonce or pre-hashed bundles.

2. Subresource Integrity (SRI)

  • CI computes hashes for every script/style/wasm asset.
  • SRI hashes injected into:
    • microfrontend.json manifest.
    • Shell loader scripts.
  • Build fails if assets lack integrity metadata.

3. Cross-Origin Resource Sharing (CORS)

  • Registry-driven allowlist: only shell origins per tenant/edition.
  • No * wildcards.
  • Declarative cors.rules.json synced with ECS/AppConfig.
  • Enforced by BFF/YARP, not browser defaults.

4. Secure Asset Loading

  • All MFEs must load over HTTPS only.
  • Shell loader rejects manifests with http:// URLs.
  • Assets cached with immutable versioned paths (/v1.2.3/app.wasm).
  • No dynamic eval or Function() allowed in adapters.

5. NuGet Signing

  • All .nupkg produced with:
    • Strong-name signing.
    • Repository signatures via Azure DevOps key vault certs.
  • Pipelines verify signatures before publishing.
  • Unsigned packages rejected at catalog ingestion.

6. SBOM (Software Bill of Materials)

  • Every build emits CycloneDX + SPDX SBOM:
    • Dependency graph
    • Licenses
    • Vulnerability scan results
  • Artifacts stored in artifacts/sbom/ and published with releases.
  • Integrates with Dependabot + GitHub Advanced Security or Azure Security Center.

🧱 Alignment with the Factory

flowchart TD
  Repo["πŸ“¦ Repo (Template Code)"]
  CI["βš™οΈ CI/CD Pipeline"]
  Policies["πŸ›‘οΈ Security Policies"]
  Artifacts["πŸ“‘ Compliance Artifacts"]

  Repo --> CI
  CI --> Policies
  CI --> Artifacts
  Policies --> Shell["πŸ›οΈ App Shell Runtime Enforcement"]
  Artifacts --> Audit["πŸ” Audit / Security Review"]
Hold "Alt" / "Option" to enable pan & zoom

πŸ“¦ Outputs

Artifact Purpose
csp.config.json Baseline CSP headers for shell apps
sri-manifest.json Integrity hashes for CDN assets
cors.rules.json Tenant/edition-aware allowlist
Signed .nupkg Verified supply chain packages
sbom.json + sbom.xml Full dependency & license manifest for audits

πŸ§ͺ Testing & QA Anchors

  • CSP smoke tests: Playwright verifies blocked inline <script>.
  • SRI validation: remove hash β†’ loader rejects.
  • CORS enforcement: origin mismatch β†’ 403.
  • NuGet check: unsigned package β†’ CI fails.
  • SBOM validation: schema + CVE scan run on every release.

βœ… Guarantees

  • No inline code; CSP strictly enforced.
  • All assets integrity-checked before execution.
  • CORS is explicit; no wildcard origins.
  • NuGet artifacts signed and verifiable.
  • Every release emits SBOMs for compliance & security audits.

πŸ” Compliance is continuous. Every repo, build, and runtime inherits these guarantees automatically.


🌍 Localization & Multi-Language Strategy

🎯 Purpose

Enable multi-language, multi-directional (LTR/RTL) support across Component Library, MFEs, and Shell with consistent localization contracts. This ensures that every ConnectSoft SaaS portal can reach global audiences with accessible, culturally aware UX.

πŸ’‘ Localization is not translation β€” it’s cultural adaptation, built in as a first-class capability.


🌐 Problem Context

Current risks without a strategy:

  • ⚠️ Hard-coded strings β†’ non-translatable UX.
  • ⚠️ No RTL awareness β†’ broken layouts for Hebrew/Arabic.
  • ⚠️ Locale drift β†’ inconsistent date/number formats across MFEs.
  • ⚠️ Missing resource synchronization β†’ MFEs and Shell use different terms for the same concept.

Consequence: fractured, inaccessible experiences for non-English tenants.


🎯 Goals

  • Resource-driven strings across all templates (RESX + IStringLocalizer).
  • RTL-aware tokens for spacing, alignment, and flow.
  • Culture propagation: Shell sets lang + dir attributes, MFEs auto-sync.
  • Pluralization & formatting with System.Globalization and ICU patterns.
  • Tenant-specific defaults loaded from ECS/AppConfig.

🧱 Contracts

Shared Resource Pattern

@inject IStringLocalizer<Resources> T
<h1>@T["WelcomeMessage"]</h1>

Resources.resx

<data name="WelcomeMessage" xml:space="preserve">
  <value>Welcome to the Portal</value>
</data>
<data name="WelcomeMessage" xml:lang="ru">
  <value>Π”ΠΎΠ±Ρ€ΠΎ ΠΏΠΎΠΆΠ°Π»ΠΎΠ²Π°Ρ‚ΡŒ Π² ΠΏΠΎΡ€Ρ‚Π°Π»</value>
</data>
<data name="WelcomeMessage" xml:lang="he">
  <value>ברוכים הבאים ΧœΧ€Χ•Χ¨Χ˜Χœ</value>
</data>

Culture Propagation

  • Shell applies:

    • <html lang="he" dir="rtl">
    • Attributes forwarded to MFEs via Custom Element host.
  • MFEs listen for lang and dir attribute changes β†’ rerender content accordingly.

// in Custom Element host
const observer = new MutationObserver(muts => {
  for (const m of muts) {
    if (m.attributeName === "lang" || m.attributeName === "dir") {
      this.updateCulture(document.documentElement.lang, document.documentElement.dir);
    }
  }
});
observer.observe(document.documentElement, { attributes: true });

πŸ“ RTL-Aware Tokens

  • Use logical properties: margin-inline-start instead of margin-left.
  • Tokens define spacing by flow (--cs-space-inline, --cs-space-block).
  • Tailwind preset maps tokens β†’ logical CSS.
.cs-card {
  padding-inline: var(--cs-space-4);
  padding-block: var(--cs-space-3);
}

πŸ“Š Formatting & Pluralization

  • Dates/numbers: use CultureInfo.CurrentCulture.
  • Plural forms handled via helpers:
public static string Plural(this IStringLocalizer T, string key, int count) =>
  T[$"{key}_{(count == 1 ? "One" : "Other")}", count];
  • Example keys: UserCount_One, UserCount_Other.

πŸ“¦ ECS/AppConfig Integration

  • Tenant default language set in ECS:
    • "tenant:1234": { "defaultCulture": "he-IL" }
  • Shell applies defaults on login/session init.
  • Users can override preference in Settings (stored per profile).

πŸ§ͺ Testing & QA Anchors

  • Snapshot tests: key pages in EN, RU, HE.
  • Visual regression: check for overflow with long translations.
  • RTL regression: ensure nav/blades invert properly.
  • Fallbacks: missing keys β†’ fall back to EN with telemetry warning.
  • A11y: screen readers correctly announce localized strings.

πŸ“Š Observability

  • Telemetry attaches culture and ui.dir to user actions.
  • Metrics:
    • cs_shell_users_by_culture
    • cs_mfe_localization_missing_keys_total

βœ… Guarantees

  • All templates ship with multilingual, RTL-ready scaffolds.
  • Shell enforces culture & direction propagation; MFEs auto-sync.
  • Formatting and pluralization are consistent across domains.
  • Tenants can set defaults in ECS, and users can override.
  • No component is accepted into the library without localization hooks.

πŸ§ͺ Testability & QA Anchors

🎯 Purpose

Establish a factory-wide test strategy and enforceable QA gates for the Component Library, MFEs, and Shell. The goal is to make regressions hard to introduce and easy to detect, with practical coverage and resilience scenarios baked into CI.

πŸ’‘ Quality isn’t a phase β€” it’s a property of the templates we generate.


🧭 Test Strategy Overview

Layer Primary Focus Tools
Component Library (RCL) Rendering, state transitions, a11y, i18n MSTest + bUnit, axe
Microfrontends (WASM CE) CE mount/unmount, manifest contract, shell-bus MSTest + bUnit, contract tests, DOM events
Application Shell Navigation, blades, MFE composition Playwright (E2E), a11y checks
Cross-Cutting Faults, timeouts, network drops Resilience tests, chaos hooks
Pipelines Coverage, flake control, mutation sanity Coverage gates, retry policy, mutation sample

πŸ“Œ We test behavior contracts first, visuals second. Snapshots are minimal and purposeful.


🧱 Unit & Rendering Tests (RCL & MFEs)

bUnit + MSTest patterns

[TestClass]
public class ModalTests : BunitTestContext
{
  [TestMethod]
  public void Opens_and_traps_focus()
  {
    var cut = RenderComponent<Modal>(p => p.Add(mt => mt.IsOpen, true));
    cut.Find("[role='dialog']").Should().NotBeNull();
    // assert focus trap
  }

  [TestMethod]
  public void Honors_localization_and_rtl()
  {
    Services.AddLocalization();
    SetCulture("he-IL"); // helper
    var cut = RenderComponent<Button>(p => p.AddChildContent("Χ©ΧžΧ•Χ¨"));
    cut.Markup.Contains("dir=\"rtl\"");
  }
}

Guidelines

  • Prefer semantic assertions (roles, labels, classes from tokens) over brittle HTML snapshots.
  • Cover controlled/uncontrolled modes, cancellable events, async flows.
  • bUnit tests for MFEs focus on bootstrap and CE lifecycle (mount/detach + re-render on theme/culture).

πŸ§ͺ Contract Tests (MFEs ⇄ Shell)

Why: Ensure manifests and bus events remain compatible over time.

Contracts

  • Manifest schema validation (JSON Schema).
  • Capability gates enforced (deny unauthorized commands).
  • Event/command names and payload shapes are stable.
[TestMethod]
public async Task Bus_Rejects_OpenBlade_When_Capability_Missing()
{
  var shell = new FakeShellHost(capabilities: new[] { "notify" });
  var mfe = new TestCustomElement(shell);
  var result = await mfe.TryOpenBladeAsync("billing.invoice");
  result.Should().BeFalse();
}

🎭 End-to-End (Shell)

Playwright suite

  • Keyboard navigation: left rail, top bar, blade open/close.
  • MFE composition: manifest load (SRI ok), CE instantiation, event roundtrip.
  • Deep links: refresh rebuilds blade stack.
  • A11y sweep: run axe on key routes; fail on AA violations.

Resilience scenarios

  • Drop SignalR β†’ banner shows, reconnect succeeds, no duplicated events.
  • BFF returns 5xx β†’ toast + retry/backoff UI, user unharmed.
  • CDN asset missing SRI β†’ shell blocks load, logs security event.

🧯 Failure & Chaos Tests

  • Network faults: inject latency, timeouts, and aborts for /bff/*.
  • Partial outages: one cluster (e.g., billing) degraded; shell remains responsive.
  • Client reloads: stress open/close blades, theme switches, culture flips.
  • Memory leaks: mount/unmount MFEs in a loop; assert GC/handle stability via browser heap snapshots (CI optional).

⚠️ Chaos hooks are guarded by env flag; never run in prod pipelines.


πŸ“ˆ Coverage & Gates

Targets (minimums)

  • RCL unit/render: 85% lines, 80% branches for public components.
  • MFE unit/contract: 80% lines.
  • Shell E2E critical flows: scenario coverage defined (not line-based):
    • nav keyboard traversal,
    • first MFE load,
    • blade deep-link restore.

CI Gates

  • Coverage thresholds enforced; failures block release stages.
  • API Surface Check: public API diff must be reviewed on changes.
  • A11y Gate: axe violations fail unless explicitly waived (tracked issue).
  • Security Gate: manifests missing SRI or CSP misconfig β†’ fail.

πŸ§ͺ Flakiness & Retry Policy

  • Single retry for networked E2E tests (idempotent only).
  • Quarantine tag moves flaky tests off the blocking path; auto-ticket created.
  • Max 7 days in quarantine before escalation.
# Example Playwright CI step
- script: npx playwright test --reporter=line --config=e2e.config.ts || npx playwright test --last-failed

🧰 Test Data & Fixtures

  • BFF stubs for billing/catalog with deterministic payloads.
  • SignalR test hub in-process for contract tests.
  • Tokenless auth in CI (OIDC stub) for login flows.
  • Localization fixtures: EN/RU/HE strings; RTL toggles.

🧷 Visual Regression (Optional, Targeted)

  • Use per-component image snapshots (Button variants, Modal states) behind an opt-in flag.
  • Only for high-value visuals with stable output (tokens minimize churn).
  • Store baselines in artifact storage; diff threshold small but non-zero.

πŸ”„ Mutations & Static Analysis

  • Mutation sample: run on one representative component suite to detect assertion weakness.
  • Roslyn analyzers: ban inline styles, enforce tokens, forbid browser token usage, JS interop sync calls.
  • Lint: Tailwind class validator, i18n missing keys detector.

🧩 Pipeline Structure

  1. Unit/Render (fast) β†’ bUnit, MSTest.
  2. Contracts β†’ manifest/bus, SRI/CSP validators.
  3. E2E β†’ Playwright headless (Desktop Chromium + WebKit matrix).
  4. A11y β†’ axe run on major routes.
  5. Coverage merge β†’ thresholds.
  6. Security β†’ SBOM, vulnerability scan, CSP/SRI check.
  7. Publish (if all green) β†’ NuGet/CDN artifacts.

πŸ“Š Reporting & Triage

  • PR comments with coverage deltas, axe results, and E2E GIFs (optional).
  • Flake dashboard: failure heatmap by test and day.
  • Defect taxonomy: classify regressions (a11y, i18n, performance, security, behavior).

βœ… Guarantees

  • Each template is testable by design with ready-to-run suites.
  • CI enforces coverage, a11y, security, and contract gates.
  • Failures are actionable with deterministic fixtures and clear triage.
  • Resilience is proven, not assumed β€” common failure modes are exercised regularly.

πŸ“š Documentation & Developer Experience

🎯 Purpose

Empower developers to adopt, extend, and maintain the ConnectSoft Blazor Templates with self-explanatory documentation, onboarding guides, and interactive showcases. Every template (Component Library, Microfrontend, Application Shell) must teach itself to new engineers, ensuring consistent factory adoption and rapid productivity.

πŸ’‘ DX is a feature. Documentation is not a side artifact, but a generated and curated part of the template itself.


🌍 Problem Context

Without structured documentation:

  • ⚠️ Developers struggle to understand template conventions.
  • ⚠️ Onboarding slows due to tribal knowledge.
  • ⚠️ UI components lack discoverability or examples.
  • ⚠️ ADRs (Architecture Decision Records) are scattered or missing.
  • ⚠️ No centralized gallery for interactive component exploration.

Consequence: slower delivery, inconsistent implementation, and higher support burden.


🎯 Goals

  • MkDocs Documentation Each repo ships with a docs/ folder + MkDocs pipeline for publishing internal sites.

  • ADRs (Architecture Decision Records) Use Log4Brains or similar to capture architectural trade-offs in docs/adr/.

  • Storybook-like Gallery Component libraries expose a browsable UI sandbox for visual exploration and testing.

  • Quickstarts & Tutorials Ready-to-run samples (/samples) to demonstrate setup, extension, and integration.

  • Onboarding Guides Human + agent-friendly walkthroughs for each template, including architecture diagrams and commands.


πŸ“ Documentation Layers

Layer Artifact Purpose
Core Docs README.md, docs/index.md Overview, getting started
Blueprint Docs blueprint-trace.md, feature-map.md Traceability to factory design
ADRs /docs/adr/* Decisions (UI kit adapter, API contracts, etc.)
Gallery /storybook or /gallery Interactive component showcase
Samples /samples/basic-app, /samples/mfe-shell Quickstart entry points
API Docs XML docs β†’ DocFX/MkDocs integration Reference for contracts, adapters

🧱 Developer Experience Anchors

  1. Onboarding Flow

    • docs/onboarding.md covers setup, build, run, and extension.
    • Diagrams (mermaid, plantuml) for repo structure.
    • Expected time-to-hello-world: <30 minutes.
  2. Component Gallery (Storybook-like)

    • Uses bUnit + Playwright snapshots for Blazor.
    • Optional MudBlazor/Flowbite live preview in sandbox mode.
  3. CLI Quickstart

    • dotnet new csblazor-component -n MyButtonLib
    • Output includes docs + gallery starter.
  4. DX Automation

    • PR bot ensures new components include:
      • XML docs
      • Gallery story
      • ADR (if architectural change)
    • Failing builds if missing.

πŸ“¦ Repository Outputs

Each repo includes:

/docs/
  index.md
  onboarding.md
  architecture.md
  adr/
    0001-ui-kit-adapter.md
    0002-feature-flags.md
  gallery/
    Button.razor.story.md
  quickstarts/
    shell-sample.md
    mfe-sample.md
README.md

πŸ§ͺ QA for Docs

  • CI pipeline lints markdown and ADRs.
  • Playwright validates gallery loads and renders.
  • ADR completeness check: no open issue without ADR reference.
  • Coverage check: every public component has at least one story + doc.

βœ… Guarantees

  • Every template repo ships with documentation baked in.
  • ADRs are first-class citizens; decisions are traceable over time.
  • Interactive gallery ensures discoverability of components and layouts.
  • Quickstarts + onboarding reduce ramp-up friction.
  • DX is enforced by automation, not optional human effort.

πŸ”„ Release & Distribution Pipelines

🎯 Purpose

Define factory-standard release pipelines for the Component Library, MFEs, and Application Shell. Pipelines must build, sign, validate, and distribute artifacts automatically, producing NuGet packages, CDN bundles, and compliance outputs ready for enterprise consumption.

πŸ’‘ A template isn’t real until it ships. Pipelines make delivery repeatable, secure, and traceable.


🌍 Problem Context

Without controlled release flows:

  • ⚠️ Packages may publish unsigned or untested.
  • ⚠️ CDN assets risk tampering or drift.
  • ⚠️ SBOMs and compliance artifacts are missing.
  • ⚠️ Multi-tenant SaaS teams must reinvent CI/CD logic for each repo.

Consequence: fragile, inconsistent releases that fail enterprise checks.


🎯 Goals

  • Unified Pipeline Templates (Azure DevOps YAML, GitHub Actions fallback).
  • End-to-end automation: build β†’ test β†’ sign β†’ validate β†’ publish.
  • Multi-channel distribution: NuGet feeds, CDN, internal artifact store.
  • Compliance built-in: SBOM, SRI, NuGet signing, coverage reports.
  • Version governance: SemVer enforced, changelog generated.

πŸ“ Pipeline Stages

  1. Build

    • Compile RCL, MFEs, and Shell.
    • Generate tokens, manifests, and docs.
  2. Test

    • Run unit, rendering, contract, and E2E tests.
    • Enforce coverage thresholds.
  3. Security & Compliance

    • CSP/SRI validation.
    • SBOM generation + vulnerability scan.
    • NuGet signing validation.
  4. Package & Sign

    • .nupkg with strong-name + repo signatures.
    • CDN bundles hashed and SRI-manifested.
  5. Publish

    • Push NuGet β†’ internal feed + NuGet.org (stable only).
    • Upload CDN bundles β†’ Azure Storage + global CDN.
    • Publish docs β†’ Azure Static Web Apps / GitHub Pages.
  6. Tag & Release

    • Git tag + annotated changelog.
    • Release notes built from ADRs + PR metadata.

πŸ“¦ Distribution Channels

NuGet

  • Component Library β†’ ConnectSoft.Blazor.ComponentLibrary
  • Microfrontend Helpers β†’ ConnectSoft.Blazor.MFE
  • Shell Base β†’ ConnectSoft.Blazor.AppShell

CDN

  • MFEs: versioned bundles /cs.billing/1.2.3/entry.js
  • SRI manifest published alongside

Docs

  • Hosted via Azure Static Web Apps, behind auth for internal repos

Artifacts

  • Stored in artifacts/ per build:
    • /packages/*.nupkg
    • /cdn/* (entry.js, wasm, css, manifest.json)
    • /docs/* (site.zip)
    • /compliance/sbom.json

πŸ”‘ Governance & Versioning

  • SemVer enforced by pipeline:
    • Breaking change β†’ major bump.
    • Features β†’ minor bump.
    • Fixes β†’ patch bump.
  • Changelog generated from commits + ADR refs.
  • Release Approval gates:
    • Pre-release β†’ internal feed only.
    • Stable β†’ NuGet.org + CDN.

πŸ§ͺ QA Anchors

  • Pipeline fails on:
    • Coverage < threshold.
    • Missing SRI for assets.
    • Unsigned NuGet packages.
    • SBOM schema invalid.
  • Dry-run mode for developers: local pipeline simulation.
  • Canary release path: deploy to next tag for validation in staging portal.

πŸ“Š Observability of Pipelines

  • Emit pipeline metrics:
    • cs_ci_build_duration_seconds
    • cs_ci_test_failures_total
    • cs_release_artifacts_published_total
  • Dashboards: release frequency, lead time, failure rate, MTTR (align with DORA metrics).

βœ… Guarantees

  • Every template has a ready-to-clone release pipeline.
  • Releases are signed, versioned, and integrity-verified.
  • Distribution spans NuGet, CDN, Docs, and Compliance artifacts.
  • Governance rules (SemVer, ADRs, coverage, SBOM) are automated in CI/CD.
  • Devs spend time on features, not on reinventing pipelines.

πŸ“– Summary & Conclusion

The ConnectSoft Blazor Templates provide a factory-standard foundation for building SaaS-ready portals with:

  • Reusable Component Libraries (RCL) β†’ token-driven, UI-kit agnostic, accessible by default.
  • Microfrontend Libraries β†’ WASM packaged as Custom Elements, manifest-driven, secure, observable.
  • Application Shell β†’ Portal-grade Blazor Web App with navigation, blades, theming, edition awareness, and tenant policies.

Across the document, we defined architecture, contracts, security, observability, testability, and developer experience. Each template is not just scaffolding β€” it is compliance-hardened, observable, localizable, and ready for enterprise distribution.

πŸ’‘ From vision to release, the templates guarantee consistency, traceability, and velocity across all frontend efforts in the ConnectSoft AI Factory.


πŸ“š References

πŸ”— Internal Factory Blueprints

  • Frontend Developer Agent
  • UI Component Library Generator Agent
  • Frontend Blueprint & Libraries (supporting files in this repo)

πŸ—οΈ Architecture & Microfrontends

🎨 UI Kits

πŸ” Security & Compliance

πŸ“ˆ Observability

πŸ§ͺ Testing & QA