docs
Some checks failed
Architecture Governance / architecture (push) Has been cancelled

This commit is contained in:
sdarbinyan
2026-07-05 04:23:47 +04:00
parent c901ec1e49
commit 10251f2fc6
24 changed files with 2424 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
# Backend Platform Architecture
## 1. System Overview
This platform is a domain-based multi-tenant SaaS marketplace.
- Frontend (Angular) is configuration-driven.
- Backend (Node.js) is tenant-aware and resolves tenant by request domain.
- UI composition is delivered by `GET /bootstrap` from the backend CONFIG DOMAIN.
- Business operations are delivered by existing BUSINESS DOMAIN APIs (`/auth`, `/items`, `/categories`, `/orders`, `/cart`, `/payments`).
Core architectural rule:
- No projectName-based behavior.
- No environment-based business branching.
- Runtime behavior is tenant-driven by domain + tenant configuration.
## 2. Tenant Resolution by Domain
Tenant identity is resolved from the incoming host:
- `shop-a.example.com` -> tenant A
- `shop-b.example.com` -> tenant B
Resolution output is attached to request context and used by:
- Config domain (`/bootstrap`, `/pages/:slug`)
- Business APIs (data isolation and policy checks)
## 3. CONFIG DOMAIN vs BUSINESS DOMAIN
### CONFIG DOMAIN
Purpose: return public runtime configuration for frontend composition.
Includes:
- tenant metadata (public)
- theme
- layout mode
- widget registry metadata
- page/section/widget structure
- footer/static pages metadata
- supported locales/currencies
- endpoint mapping (public)
### BUSINESS DOMAIN
Purpose: transactional and catalog operations.
Includes:
- authentication/session
- products/items
- categories
- cart
- orders
- payments
Boundary rule:
- BUSINESS APIs do not return UI layout/theme/widget composition.
- CONFIG APIs do not return transactional business state.
## 4. Bootstrap API Role
`GET /bootstrap` initializes frontend runtime.
Backend responsibilities:
1. Resolve tenant from domain.
2. Load tenant config aggregate.
3. Return versioned, public bootstrap payload.
4. Never leak secrets in bootstrap.
Frontend responsibilities:
1. Load bootstrap at startup.
2. Render based on config only.
3. Use business APIs only for domain data/actions.
## 5. Existing API Domains (Unchanged)
- `/auth`
- `/items`
- `/categories`
- `/orders`
- `/cart`
- `/payments`
These APIs remain authoritative for business workflows and must not be rewritten for UI composition.
## 6. Data Flow Diagram (Text)
```text
Browser Request
-> Edge/Ingress (Host preserved)
-> Node.js API Gateway
-> Tenant Resolver Middleware (host -> tenant)
-> Request Context Enrichment (tenantId, locale, policy)
-> Route Dispatch
-> /bootstrap (CONFIG DOMAIN) -> Config Services -> Response JSON
-> /items|/orders|... (BUSINESS DOMAIN) -> Business Services -> Response JSON
<- Tenant-scoped response
```
## 7. Request Lifecycle (Browser -> Backend -> Tenant -> Response)
1. Browser sends request with `Host` header.
2. Backend middleware resolves tenant by domain.
3. Backend validates tenant status (active, allowed, mapped).
4. Tenant context is attached to request (`req.ctx.tenant`).
5. Route handler executes with tenant-scoped repositories/services.
6. Response is returned with tenant-scoped data.
## 8. Scalability Notes (10100+ Tenants)
- Keep tenant config in low-latency cache with invalidation.
- Use stateless API instances; tenant context is per request.
- Enforce strict tenant filters at repository/query layer.
- Monitor by tenant dimensions (latency, errors, saturation).
- Apply rate limits and abuse controls per tenant/domain.

View File

@@ -0,0 +1,151 @@
# Bootstrap API Specification
## 1. Endpoint
- Method: `GET`
- Path: `/bootstrap`
- Auth: public (or optional lightweight token), tenant-scoped by domain
## 2. Domain-Based Request Flow
1. Receive request with host.
2. Resolve tenant by host.
3. Load tenant config aggregate from CONFIG DOMAIN.
4. Build versioned bootstrap payload.
5. Return public config JSON.
Failure responses:
- `404` unknown tenant domain
- `403` tenant inactive/suspended
- `500` config assembly failure
## 3. Production-Like Sample Response
```json
{
"schemaVersion": "2.1.0",
"generatedAt": "2026-07-05T10:30:00Z",
"tenant": {
"id": "a95c2f1b-58c1-4d8b-b35b-82e5bdf14321",
"slug": "alpha-market",
"code": "ALPHA",
"host": "shop.alpha.example.com",
"name": "Alpha Marketplace",
"defaultLocale": "en",
"supportedLocales": ["en", "ru", "hy"],
"defaultCurrency": "USD",
"supportedCurrencies": ["USD", "EUR", "AMD"],
"timezone": "UTC"
},
"theme": {
"themeId": "alpha-light",
"mode": "light",
"palette": {
"primary": "#2F6F6D",
"secondary": "#9FB8B6",
"accent": "#B5D7D4",
"textPrimary": "#1E3C38",
"textSecondary": "#5E7471",
"backgroundPrimary": "#FFFFFF",
"backgroundSecondary": "#F6F8F8",
"border": "#D7E0DF"
}
},
"layout": {
"type": "sidebar-left",
"options": {
"sidebarSticky": true,
"heroEnabled": true
}
},
"widgetRegistry": {
"manifestUrl": "/config/widgets/manifest.json"
},
"pages": [
{
"id": "page-home",
"key": "home",
"route": { "path": "/", "exact": true },
"layout": { "type": "carousel-home" },
"sections": [
{
"id": "sec-hero",
"type": "hero",
"order": 1,
"widgets": [
{
"id": "w-hero-main",
"type": "hero",
"version": "1.0.0",
"order": 1,
"padding": "0.5rem 0",
"visibility": { "desktop": true, "tablet": true, "mobile": true },
"props": { "title": "Welcome", "subtitle": "B2B Catalog" }
}
]
}
]
}
],
"footer": {
"paymentIcons": [
{ "src": "/assets/payments/visa.svg", "alt": "Visa", "width": 40, "height": 28 },
{ "src": "/assets/payments/mastercard.svg", "alt": "Mastercard", "width": 40, "height": 28 }
],
"copyrightText": {
"en": "© 2026 Alpha Marketplace. All rights reserved.",
"ru": "© 2026 Alpha Marketplace. Все права защищены.",
"hy": "© 2026 Alpha Marketplace. Բոլոր իրավունքները պաշտպանված են:"
},
"legalPageKeys": ["about-us", "privacy-policy", "terms-of-service"]
},
"localization": {
"defaultLocale": "en",
"supportedLocales": ["en", "ru", "hy"],
"currencyByLocale": {
"en": "USD",
"ru": "USD",
"hy": "AMD"
}
},
"apiEndpoints": {
"bootstrap": { "path": "/bootstrap", "method": "GET", "timeoutMs": 5000 },
"website": {
"items": { "path": "/items", "method": "GET" },
"categories": { "path": "/categories", "method": "GET" },
"cart": { "path": "/cart", "method": "GET" },
"orders": { "path": "/orders", "method": "POST" },
"payments": { "path": "/payments", "method": "POST" }
}
}
}
```
## 4. Field-by-Field Meaning
- `schemaVersion`: bootstrap contract version used by frontend parser.
- `generatedAt`: payload generation timestamp.
- `tenant`: public tenant identity and locale/currency defaults.
- `theme`: UI tokens; no business logic.
- `layout.type`: global layout mode. Supported: `default`, `sidebar-left`, `carousel-home`, `minimal`.
- `widgetRegistry.manifestUrl`: source for widget definitions/components mapping metadata.
- `pages`: route-driven composition graph.
- `footer`: footer links/icons/legal references.
- `localization`: supported locales and currency mapping.
- `apiEndpoints`: public endpoint mapping for frontend clients.
## 5. Versioning Strategy
Use semantic versioning in `schemaVersion`:
- Patch (`2.1.1`): non-breaking metadata additions.
- Minor (`2.2.0`): additive fields/sections with backward compatibility.
- Major (`3.0.0`): breaking structural changes.
Operational rules:
- Keep old parser compatibility for at least one minor line.
- Publish migration notes for any major bump.
- Validate payload against schema before release.
## 6. Security Rules
Never include in bootstrap:
- private keys
- internal credentials
- admin secrets
- payment signing material
Bootstrap is strictly public runtime configuration.

View File

@@ -0,0 +1,118 @@
# Business APIs
## 1. Scope
Business APIs provide transactional and catalog capabilities.
They must remain tenant-aware and must not return UI/layout/theme/widget configuration.
## 2. Immutable Rule
These APIs MUST NOT return:
- page composition
- layout mode
- widget metadata
- theme/footer/static page config
That data belongs to CONFIG DOMAIN (`/bootstrap`, `/pages/:slug`).
## 3. API Catalog
## /auth
Purpose:
- session creation/validation
- login/logout flows
- token refresh where applicable
High-level response shape:
- session/token metadata
- user identity claims
- permission scopes
Tenant rule:
- auth sessions are tenant-scoped by request context.
Must not change:
- authentication contract and downstream payment/auth integrations.
## /items
Purpose:
- list/fetch product items
- search/filter/sort
- item details and availability
High-level response shape:
- item arrays / item objects
- pagination metadata
- stock/price fields
Tenant rule:
- only items visible to tenant catalog policy.
Must not change:
- item identifiers/price semantics relied on frontend checkout/cart logic.
## /categories
Purpose:
- category tree retrieval
- category filtering metadata
High-level response shape:
- hierarchical or flat category collections
- visibility and ordering metadata
Tenant rule:
- category graph resolved per tenant catalog configuration.
Must not change:
- category IDs and parent linkage semantics consumed by frontend domain mapping.
## /orders
Purpose:
- create and track orders
- lifecycle state transitions
High-level response shape:
- order id
- status
- totals and line-items
Tenant rule:
- order creation/query restricted to tenant context.
Must not change:
- order status lifecycle contract integrated with payment and notification flows.
## /cart
Purpose:
- cart synchronization and server-side cart state where applicable
High-level response shape:
- cart items
- totals
- selected delivery/payment metadata
Tenant rule:
- cart state must be isolated by tenant + session/user.
Must not change:
- cart schema expected by checkout and payment request builders.
## /payments
Purpose:
- payment intent/QR/card flow initiation
- payment status querying
High-level response shape:
- payment id/reference
- redirect/QR links
- status fields
Tenant rule:
- payment credentials/routes resolved per tenant context on backend.
Must not change:
- existing payment provider contracts and callback/status semantics.
## 4. Governance for All Business APIs
- tenant derived from request context only
- strict repository-level tenant filtering
- no UI config payloads
- backward compatibility for existing frontend business flows

View File

@@ -0,0 +1,75 @@
# Config Domain
## 1. Purpose
CONFIG DOMAIN provides runtime UI configuration for a tenant.
It enables one frontend build to serve many tenants by changing configuration, not code.
Primary outputs:
- `/bootstrap`
- `/pages/:slug` (static content domain)
## 2. Ownership
Config domain owns:
- tenant public runtime metadata
- theme and visual tokens
- layout mode and page structure
- widget registry metadata pointers
- footer metadata and legal-page mapping
- feature flags and localization mappings
Business domain owns:
- items, categories, cart, orders, payments, auth
## 3. Layout Engine Responsibility
Backend returns layout intent (e.g., `layout.type`) and page graph.
Frontend layout engine composes UI from this graph.
Supported modes:
- default
- sidebar-left
- carousel-home
- minimal
No tenant-specific UI branching in component code.
## 4. Widget Registry Concept
Backend provides `widgetRegistry.manifestUrl`.
Frontend reads manifest and resolves approved widget keys.
Benefits:
- controlled extensibility
- unknown widget safe fallback
- decoupled rollout of widget metadata
## 5. Footer + Static Page System
Footer metadata includes:
- columns and links
- payment icons
- legal page references
- localized copyright
Static pages provide multilingual HTML per slug.
Frontend renders through safe sanitization path.
## 6. Feature Flags
Feature flags in bootstrap:
- enable/disable capabilities at tenant scope
- support gradual rollout
- avoid deployment-based behavior switches
## 7. Config Data vs Business Data
Config data:
- shapes the interface
- relatively low-frequency changes
- public-safe payloads
Business data:
- transactional/catalog state
- high-frequency updates
- operational integrity requirements
## 8. Why Separation Matters
- scalability: independent lifecycle for UI config and business operations
- safety: prevents leaking operational logic into UI composition
- maintainability: clear boundaries and lower coupling
- multi-tenant readiness: behavior changes per tenant without code fork

View File

@@ -0,0 +1,66 @@
# Backend Platform Deployment
## 1. Local Development Setup
Recommended local flow:
1. Start backend API (Node.js) with local tenant mappings.
2. Start frontend Angular app with proxy to backend.
3. Use local domains/hosts file entries for tenant simulation.
Example hosts mapping:
- `alpha.local` -> localhost
- `beta.local` -> localhost
## 2. Environment Variables (Backend)
Infrastructure-focused variables only:
- `PORT`
- `NODE_ENV`
- `DB_URL`
- `REDIS_URL`
- `TENANT_CACHE_TTL_SECONDS`
- `TRUST_PROXY`
- `ALLOWED_HOSTS`
- `LOG_LEVEL`
Guideline:
- do not use environment variables for tenant business behavior branching.
- tenant behavior comes from tenant config data resolved by domain.
## 3. Frontend Proxy Setup
Frontend proxy should route API calls to Node backend:
- `/bootstrap`
- `/pages/*`
- `/items`, `/categories`, `/cart`, `/orders`, `/payments`, `/auth`
Proxy keeps browser-side calls same-origin in local development.
## 4. Production Deployment Flow
1. Deploy stateless Node API instances.
2. Configure ingress/load balancer to preserve host headers.
3. Route all tenant domains to same backend/frontend runtime.
4. Resolve tenant by domain per request.
5. Serve tenant-specific bootstrap + tenant-scoped business responses.
## 5. Multi-Tenant Domain Mapping Strategy
Maintain authoritative mapping table:
- host -> tenantId
- tenant status
- locale/currency defaults
Operational controls:
- admin tooling for host assignment
- cache invalidation on mapping updates
- audit logs for domain changes
## 6. Scaling Considerations (10100+ Tenants)
- horizontal scale backend instances
- distributed cache for tenant + config hot paths
- query/index optimization with tenant-partitioning strategy
- per-tenant rate limiting and quotas
- observability dimensions: tenantId, host, endpoint, latency, error-rate
## 7. Reliability Checklist
- health/readiness probes
- circuit breakers for downstream services
- timeout + retry policy by endpoint class
- graceful degradation for config fetch failures
- rollback strategy for bad config releases

View File

@@ -0,0 +1,62 @@
# Static Pages System
## 1. Endpoint
- Method: `GET`
- Path: `/pages/:slug`
- Scope: tenant-aware by request domain
Supported slugs (example set):
- `about-us`
- `privacy-policy`
- `terms-of-service`
- `returns-policy`
## 2. Storage Model
Pages are stored per tenant:
- key: tenantId + slug
- multilingual content map (locale -> HTML)
- optional SEO metadata per locale
- status (published/draft)
## 3. Example Response
```json
{
"slug": "about-us",
"title": {
"en": "About Us",
"ru": "О компании",
"hy": "Մեր մասին"
},
"content": {
"en": "<h1>About Us</h1><p>...</p>",
"ru": "<h1>О компании</h1><p>...</p>",
"hy": "<h1>Մեր մասին</h1><p>...</p>"
},
"seo": {
"title": { "en": "About Us" },
"description": { "en": "Company information" }
},
"updatedAt": "2026-07-05T09:00:00Z"
}
```
## 4. Frontend Rendering Rule
Frontend renders static pages via safe HTML flow only:
- resolve locale-specific HTML
- sanitize before binding
- bind to template as trusted/safe HTML only in static page component context
## 5. Security Considerations
Backend requirements:
- content moderation/validation pipeline
- disallow dangerous tags/attributes at content publishing stage
- maintain revision history and audit trail
Frontend requirements:
- enforce sanitizer before rendering
- no raw HTML injection in arbitrary components
Platform requirements:
- tenant isolation on page retrieval
- cache with tenant+slug key
- return 404 for missing slug in tenant scope

View File

@@ -0,0 +1,85 @@
# Tenant Resolution
## 1. Goal
Resolve tenant from request domain and enforce strict tenant isolation for all config and business endpoints.
## 2. Middleware Flow
1. Parse request host (`Host`/`X-Forwarded-Host` as trusted by ingress policy).
2. Normalize host (lowercase, strip port, normalize punycode if needed).
3. Resolve tenant record from host mapping store.
4. Validate tenant status (`active`, not suspended/expired).
5. Attach tenant context to request.
6. Continue to route handlers with tenant-scoped services.
## 3. Request Context Attachment
Attach immutable context object, e.g.:
- `req.ctx.tenantId`
- `req.ctx.tenantSlug`
- `req.ctx.host`
- `req.ctx.defaultLocale`
- `req.ctx.allowedLocales`
All downstream services must read tenant from context, not from query params.
## 4. Pseudocode Middleware Example
```ts
async function tenantResolver(req, res, next) {
const host = normalizeHost(req.headers['x-forwarded-host'] || req.headers.host);
if (!host) return res.status(400).json({ error: 'INVALID_HOST' });
const cacheKey = `tenant:host:${host}`;
let tenant = await cache.get(cacheKey);
if (!tenant) {
tenant = await tenantRepository.findByHost(host);
if (tenant) await cache.set(cacheKey, tenant, { ttlSeconds: 300 });
}
if (!tenant) return res.status(404).json({ error: 'TENANT_NOT_FOUND' });
if (!tenant.active) return res.status(403).json({ error: 'TENANT_INACTIVE' });
req.ctx = {
...(req.ctx || {}),
tenantId: tenant.id,
tenantSlug: tenant.slug,
host,
defaultLocale: tenant.defaultLocale,
allowedLocales: tenant.supportedLocales
};
return next();
}
```
## 5. Caching Strategy
Recommended:
- L1 in-memory cache per API instance for hot host lookups.
- L2 distributed cache (Redis) for cross-instance consistency.
- Cache key by host.
- Short TTL (60-300s) + explicit invalidation on tenant changes.
Do not cache authorization decisions globally; only cache tenant mapping metadata.
## 6. Security Rules
- Never trust tenant from client payload.
- Always derive tenant from validated host/context.
- Enforce tenant filter at repository layer for every query.
- Reject cross-tenant IDs even if resource exists globally.
- Emit audit logs for tenant mismatch attempts.
## 7. Edge Cases
### Unknown Domain
- Behavior: return `404 TENANT_NOT_FOUND`.
- Optional: redirect only if explicit global fallback policy exists.
### Inactive Tenant
- Behavior: return `403 TENANT_INACTIVE`.
- Optional: include support contact metadata in response.
### Host Header Poisoning
- Use trusted proxy chain rules.
- Ignore untrusted forwarded host headers.
### Local Development Domains
- Keep explicit local host mapping table.
- No projectName shortcuts for tenant selection.