nical Implementation (TypeScript)
Asset Definition Schema
import { z } from 'zod';
// Semantic Versioning Schema
const SemVerSchema = z.string().regex(/^\d+\.\d+\.\d+$/);
// Dimension Key-Value Pair
const DimensionSchema = z.record(z.string());
// Dependency Graph Node
const DependencySchema = z.object({
assetId: z.string(),
versionRange: z.string(), // e.g., "^1.2.0"
type: z.enum(['required', 'optional']),
});
// Core Asset Definition
export const AssetDefinitionSchema = z.object({
id: z.string().uuid(),
type: z.enum(['api', 'schema', 'ui', 'rule', 'config']),
version: SemVerSchema,
dimensions: DimensionSchema,
dependencies: z.array(DependencySchema),
schema: z.any(), // JSON Schema for validation
metadata: z.object({
owner: z.string(),
lifecycle: z.enum(['draft', 'active', 'deprecated', 'archived']),
tags: z.array(z.string()),
}),
});
export type AssetDefinition = z.infer<typeof AssetDefinitionSchema>;
Matrix Registry Service
import { InMemoryAssetStore } from './store';
import { AssetDefinition, AssetDefinitionSchema } from './types';
export class AssetMatrixRegistry {
private store: InMemoryAssetStore<AssetDefinition>;
constructor() {
this.store = new InMemoryAssetStore();
}
/**
* Registers a new asset version.
* Enforces immutability: existing versions cannot be overwritten.
*/
async registerAsset(asset: AssetDefinition): Promise<void> {
const validationResult = AssetDefinitionSchema.safeParse(asset);
if (!validationResult.success) {
throw new Error(`Invalid asset definition: ${validationResult.error.message}`);
}
const key = `${asset.id}@${asset.version}`;
if (this.store.has(key)) {
throw new Error(`Asset version ${key} already exists. Immutability violation.`);
}
this.store.set(key, asset);
await this.validateDependencies(asset);
}
/**
* Resolves the best asset version based on dimensions and version range.
*/
async resolveAsset(
assetId: string,
versionRange: string,
dimensions: Record<string, string>,
): Promise<AssetDefinition> {
const candidates = this.store.filter(
(key, asset) =>
asset.id === assetId &&
matchesVersionRange(asset.version, versionRange) &&
matchesDimensions(asset.dimensions, dimensions),
);
if (candidates.length === 0) {
throw new Error(`No asset found for ${assetId} matching range ${versionRange} and dimensions.`);
}
// Return highest semantic version
return candidates.sort((a, b) => compareSemVer(a.version, b.version))[0];
}
private async validateDependencies(asset: AssetDefinition): Promise<void> {
for (const dep of asset.dependencies) {
try {
await this.resolveAsset(dep.assetId, dep.versionRange, {});
} catch (err) {
if (dep.type === 'required') {
throw new Error(`Required dependency ${dep.assetId} not satisfied.`);
}
}
}
}
}
// Helper functions for version and dimension matching
function matchesVersionRange(version: string, range: string): boolean {
// Implementation using semver library logic
return true;
}
function matchesDimensions(assetDims: Record<string, string>, requestDims: Record<string, string>): boolean {
return Object.entries(requestDims).every(
([key, value]) => assetDims[key] === value || assetDims[key] === '*'
);
}
function compareSemVer(a: string, b: string): number {
// Standard semver comparison
return 0;
}
Composition Engine
export class CompositionEngine {
private registry: AssetMatrixRegistry;
constructor(registry: AssetMatrixRegistry) {
this.registry = registry;
}
/**
* Composes a product context by resolving all required assets.
* Builds a dependency graph and resolves in topological order.
*/
async composeProductContext(
productRequirements: Array<{ id: string; versionRange: string; dimensions: Record<string, string> }>,
): Promise<Record<string, AssetDefinition>> {
const resolvedAssets: Record<string, AssetDefinition> = {};
const graph = new Map<string, Set<string>>();
// Build dependency graph
for (const req of productRequirements) {
await this.buildGraph(req, resolvedAssets, graph);
}
// Topological sort to resolve dependencies
const sortedAssets = this.topologicalSort(graph);
// Resolve assets in order
for (const assetId of sortedAssets) {
if (!resolvedAssets[assetId]) {
// This should be pre-populated by buildGraph
throw new Error(`Unresolved asset in graph: ${assetId}`);
}
}
return resolvedAssets;
}
private async buildGraph(
req: { id: string; versionRange: string; dimensions: Record<string, string> },
resolved: Record<string, AssetDefinition>,
graph: Map<string, Set<string>>,
): Promise<void> {
if (resolved[req.id]) return;
const asset = await this.registry.resolveAsset(req.id, req.versionRange, req.dimensions);
resolved[req.id] = asset;
if (!graph.has(req.id)) {
graph.set(req.id, new Set());
}
for (const dep of asset.dependencies) {
graph.get(req.id)!.add(dep.assetId);
await this.buildGraph(
{ id: dep.assetId, versionRange: dep.versionRange, dimensions: req.dimensions },
resolved,
graph,
);
}
}
private topologicalSort(graph: Map<string, Set<string>>): string[] {
const visited = new Set<string>();
const result: string[] = [];
const visit = (node: string) => {
if (visited.has(node)) return;
visited.add(node);
const neighbors = graph.get(node) || new Set();
for (const neighbor of neighbors) {
visit(neighbor);
}
result.push(node);
};
for (const node of graph.keys()) {
visit(node);
}
return result;
}
}
Architecture Decisions and Rationale
- Immutability: Assets are immutable once registered. This eliminates "works on my machine" issues and ensures reproducibility. Updates require new versions.
- Dimensional Resolution: Assets are resolved based on dimensions (e.g., region, tenant). This allows a single asset ID to have multiple implementations for different contexts without branching code.
- Dependency Negotiation: The resolver handles version ranges, allowing products to specify compatibility constraints while the matrix selects the optimal version.
- Contract-First: Schema validation is enforced at registration and runtime. Breaking changes are detected before deployment.
Pitfall Guide
1. Over-Normalization of Assets
Mistake: Breaking assets into microscopic units that increase cognitive load and resolution overhead.
Explanation: Not every function should be an asset. Over-normalization leads to "chatty" compositions and performance degradation.
Best Practice: Define assets at the granularity of business capability or reusable component. Use cohesion metrics to determine asset boundaries.
2. The "God Registry" Bottleneck
Mistake: Centralizing the registry without scaling or caching strategies, causing latency spikes.
Explanation: If every product request hits the registry synchronously, the registry becomes a single point of failure and latency bottleneck.
Best Practice: Implement client-side caching with TTL and invalidation signals. Use a CDN for static asset delivery. The registry should be the source of truth, not the runtime data path for high-frequency reads.
3. Ignoring Semantic Versioning Strictness
Mistake: Treating versioning as a formality, leading to breaking changes in minor/patch updates.
Explanation: Consumers rely on version ranges. If producers break contracts in non-major versions, the matrix resolution fails silently or causes runtime errors.
Best Practice: Enforce semantic versioning via CI/CD gates. Automate contract testing to detect breaking changes. Block deployments that violate versioning rules.
4. Dimensional Explosion
Mistake: Creating too many dimensions, resulting in sparse asset matrices and maintenance nightmares.
Explanation: Excessive dimensions (e.g., versioning by device model) make asset management unmanageable.
Best Practice: Limit dimensions to high-cardinality, stable axes like Region, Environment, and Tenant. Use feature flags for low-level variations instead of asset dimensions.
5. Lack of Drift Detection
Mistake: Assets in production drift from their registered definitions due to hotfixes or manual changes.
Explanation: Drift undermines the trust in the matrix. Products may consume assets that behave differently than expected.
Best Practice: Implement runtime integrity checks. Compare running asset hashes against the registry. Alert on drift and enforce rollback policies.
6. Security Perimeter Confusion
Mistake: Assuming assets are secure because they are governed, ignoring cross-product security implications.
Explanation: An asset consumed by multiple products may expose sensitive data if permissions are not scoped correctly.
Best Practice: Embed security policies within asset metadata. Enforce least-privilege access at the composition engine level. Audit asset access across products.
7. Testing Matrix Interactions vs. Unit Testing
Mistake: Relying solely on unit tests for assets, missing integration failures in the matrix.
Explanation: Assets may work in isolation but fail when composed with specific versions of dependencies.
Best Practice: Implement matrix integration tests that verify composition scenarios. Use contract testing to validate interactions between producers and consumers.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Startup / MVP | Lightweight Registry + Git-based Storage | Low overhead, fast iteration, sufficient for small teams. | Low infrastructure cost. |
| Enterprise / Multi-Region | Distributed Registry + CDN + Dimensional Resolution | High availability, low latency, strict governance required. | Moderate infrastructure cost, high ROI from reuse. |
| Legacy Migration | Strangler Fig Pattern with Sidecar Resolver | Minimizes risk, allows gradual migration without rewrite. | Moderate engineering cost, reduced migration risk. |
| High-Frequency Trading | In-Memory Matrix + Zero-Copy Resolution | Ultra-low latency requirements, deterministic resolution. | High optimization cost, critical for performance. |
Configuration Template
matrix.config.yaml
registry:
endpoint: "https://matrix-registry.internal"
cache:
ttl: 300s
max-size: 1000
auth:
type: "mtls"
cert-path: "/etc/certs/client.pem"
dimensions:
- name: "region"
values: ["us-east-1", "eu-west-1", "ap-south-1"]
- name: "environment"
values: ["staging", "production"]
- name: "tenant"
type: "dynamic"
source: "header:x-tenant-id"
assets:
types:
- "api"
- "schema"
- "ui"
- "rule"
validation:
strict-schema: true
enforce-semver: true
composition:
timeout: 2000ms
retry:
attempts: 3
backoff: "exponential"
fallback:
strategy: "oldest-stable"
observability:
metrics:
- "asset.resolve.latency"
- "asset.resolve.errors"
- "asset.drift.detected"
tracing:
enabled: true
sampler: 0.1
Quick Start Guide
-
Initialize Project:
npm install @codcompass/matrix-sdk
npx matrix init --config matrix.config.yaml
-
Define First Asset:
Create assets/user-schema-v1.0.0.json:
{
"id": "user-schema",
"type": "schema",
"version": "1.0.0",
"dimensions": { "environment": "*" },
"schema": {
"type": "object",
"properties": {
"id": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "email"]
}
}
-
Register Asset:
npx matrix register ./assets/user-schema-v1.0.0.json
-
Consume in Product:
import { MatrixClient } from '@codcompass/matrix-sdk';
const client = new MatrixClient({ configPath: './matrix.config.yaml' });
const schema = await client.resolveAsset('user-schema', '^1.0.0', { environment: 'production' });
console.log(schema.schema); // Validated schema object
-
Verify Integration:
Run composition tests to ensure assets resolve correctly under different dimensions and version ranges.
npx matrix test --suite composition
This architecture transforms product ecosystem building from a coordination nightmare into a scalable, automated engineering discipline. By treating digital assets as the core unit of composition and managing them through a rigorous matrix, organizations achieve rapid innovation, high reuse, and robust interoperability across their product portfolio.