Documentation

dynaplex-comprehensive-overview.md

Dynaplex: Comprehensive Platform Overview

A complete guide to the architecture, components, patterns, and tooling that make up the Acsis Dynaplex platform as implemented in the acsis-core repository.


Table of Contents

  1. What is Dynaplex?
  2. Repository Structure: The Three Layers
  3. Strata: The Foundation
  4. Engines: The Computational Cores
  5. Cortex: The Brain
  6. The Passport System (Prism)
  7. Database Architecture
  8. API Architecture
  9. Service Orchestration (.NET Aspire)
  10. Inter-Service Communication (Kiota)
  11. Authentication & Authorization
  12. Observability & Operations
  13. Build System & Developer Experience
  14. Architectural Enforcement (Analyzers)
  15. Source Generation
  16. Journal & Audit System
  17. Multi-Tenancy
  18. The AssetTrak UI
  19. Agent Infrastructure
  20. Deployment & Cloud Infrastructure
  21. ADR Index

What is Dynaplex?

Dynaplex is a software architecture and development methodology created at Acsis. It is the result of an extreme focus on developer experience, rigorously defined and enforced architecture patterns, dynamic orchestration, and powerful modularity. The acsis-core repository serves two purposes: it defines the architecture and implements it as working software.

Core Philosophy

  • Architecture and tooling are inseparable - the development environment is part of the product philosophy
  • Composition over inheritance - dependency injection, interfaces, small focused components
  • Explicit over implicit - clear data flow, compile-time enforcement, no magic
  • Incremental progress - small changes that compile and pass tests
  • Convention over configuration - manifest-driven metadata, schema discovery, consistent patterns
  • The boring solution wins - avoid clever tricks; if you need to explain it, it's too complex

Key Design Decisions

  • Microservices with process isolation via .NET Aspire (not dynamic assembly loading)
  • Database-per-schema in shared PostgreSQL (not separate database servers)
  • GUID primary keys everywhere (UUID v7) - never backward to legacy long/int
  • Type-safe inter-service communication via auto-generated Kiota clients
  • Compile-time architectural enforcement via custom Roslyn analyzers
  • Multi-tenancy as a first-class concern - explicit tenant scoping on every operation
  • Comprehensive audit trails - journal tables for all state changes

Repository Structure: The Three Layers

acsis-core/
├── strata/          # The Foundation - libraries, analyzers, generators, orchestration
├── engines/         # The Computational Cores - domain services
├── cortex/          # The Brain - agent identity, web UI, monitoring
├── projects/        # Deployable assemblies (Aspire AppHosts)
├── docs/            # Architecture docs, ADRs, tutorials, reference
├── scripts/         # Session management, utilities
└── .dplx/           # Agent configuration

Namespace pattern: Acsis.Dynaplex.{Layer}.{Component} (e.g., Acsis.Dynaplex.Engines.Catalog)


Strata: The Foundation

Strata is the pure, stateless foundation layer. It provides architectural underpinnings that all engines build upon. Strata components cannot reference engines, EF Core, ASP.NET Core, or make HTTP calls (enforced by analyzers).

Core Library (strata/core/)

The shared foundation for all engines:

  • DynaplexDbContext - Base class for all engine DbContexts. Provides multi-tenancy support (SetTenantId), system scope for cross-tenant operations (SetSystemScope), journal context (SetUserId, SetPermissionId), and tenant validation on SaveChanges
  • DatabaseExtensions - Cross-schema entity references (AddExternalReference<T>), schema isolation configuration, Passport relationship wiring, PostgreSQL snake_case naming
  • JournalTable - Base class for all audit log entities with composite key (Id, SeqId), validity tracking, and user attribution
  • ComponentSchemaRegistry - Runtime schema discovery via reflection; prevents migrations from recreating tables in wrong schemas
  • DependencyManifest - Fluent declaration of engine-to-engine dependencies
  • ITenantResolver / HttpContextTenantResolver - Tenant ID extraction from auth claims
  • ScopedDbContextFactory - Creates DbContext instances with proper tenant context
  • IReferenceDataSeeder - Seed lookup tables (UoM, platform types, etc.)

Service Defaults (strata/service-defaults/)

The centralized configuration that every ASP.NET Core service uses. One call to AddServiceDefaults() sets up:

  • OpenTelemetry - distributed tracing, metrics, structured logging
  • Health checks - readiness (/health) and liveness (/alive)
  • Azure App Configuration - engine-specific prefixes, Key Vault integration
  • Database registration - AddAcsisDbContext<T>() with tenant injection and connection pooling
  • API client registration - AddAcsisApiClient<T>() with service discovery and resilience
  • Endpoint mapping - MapAcsisEndpoints() with security headers, CORS, exception handling (RFC 7807 ProblemDetails), permission discovery (/.well-known/permissions), Scalar API docs
  • JWT Bearer auth - RSA key validation, authority-based discovery, service discovery modes
  • HTTP resilience - standard resilience handler (retry, circuit breaker, timeout) via Polly
  • Header propagation - Authorization header forwarded in service-to-service calls
  • Serilog - file logging with daily rolling, structured logging

Orchestration Extensions (strata/orchestration/)

.NET Aspire extension methods for declarative infrastructure:

  • DynaplexContext - Singleton that stores all shared infrastructure references (database, config, monitoring, storage)
  • AddEngine<TProject>(metadata) - Registers an engine with HTTP endpoint, infrastructure wiring, Azure Container App publishing, and dependency tracking
  • AddDynaplexDatabase() - PostgreSQL with tenant isolation
  • AddDynaplexAppConfiguration() - Azure App Config with Key Vault
  • AddDynaplexAppInsights() - Application Insights
  • AddDynaplexStorage() - Azure Storage accounts
  • AddDynaplexAnalytics() - Apache Superset + Redis
  • AddDynaplexNextJsApp() - Next.js UI with automatic API service URL discovery from EngineIndex

GS1/EPC Library (strata/gs1/)

Full GS1 standards implementation: EPC encoding, UPC handling, Application Identifiers, URN/URI serialization, EPCIS translation, binary partition tables. Used by supply chain operations.

HTTP Utilities (strata/http/)

Pagination (PagedResponse<T>, PaginationOptions), search models, URL utilities, proxy definitions.

Roslyn Analyzers (strata/analyzers/)

31 analyzer classes producing 88 diagnostic rules. See Architectural Enforcement.

Source Generators (strata/source-generators/)

Compile-time code generation. See Source Generation.

Project Templates (strata/project-templates/)

dotnet new templates for scaffolding new engine projects: Core, Database, Abstractions, ApiClient, DbMigrator, UnitTests, IntegrationTests. Parameterized with DPLX_COMPONENT_NAME and DPLX_COMPONENT_NAMESPACE.

ERP Integration (strata/erp-integration/)

ERP system integration abstractions. Structure established, implementation in progress.


Engines: The Computational Cores

Each engine is an independently deployable ASP.NET Core service with its own database schema, API surface, and Kiota-generated clients.

Standard Component Structure

Every engine follows this enforced pattern:

engines/{engine}/
  src/
    Acsis.Dynaplex.Engines.{Name}/                  # Service (API endpoints, business logic)
    Acsis.Dynaplex.Engines.{Name}.Abstractions/      # Interfaces, DTOs, contracts
    Acsis.Dynaplex.Engines.{Name}.Database/          # DbContext, entities, configurations
    Acsis.Dynaplex.Engines.{Name}.ApiClient/         # Kiota-generated HTTP client
  tests/
    Acsis.Dynaplex.Engines.{Name}.UnitTests/
    Acsis.Dynaplex.Engines.{Name}.IntegrationTests/

Core Foundation Engines

Prism (engines/prism/) - Schema: prism
The global identity system. Manages Passports (globally-unique IDs for all entities), Identifiers (maps platform-specific IDs to Passport IDs), Attributes (schema-flexible attribute definitions and assignments), PlatformTypes (entity type registry), and Object hierarchies (edges/ancestors). No tenant scope - system-level. See The Passport System.

Identity (engines/identity/) - Schema: identity
Authentication and authorization. JWT token management (RSA key signing), permission graph with hierarchical expansion, RBAC, external auth providers, tenant management. Permission sync runs as a background service. Dependencies: Prism, Spatial, CoreData, SystemEnvironment.

Spatial (engines/spatial/) - Schema: spatial
Location hierarchy and geocoding. Hierarchical location trees (parent-child), LocationCategories, Regions, Addresses with Azure Maps geocoding, Countries. Supports GLN (Global Location Number) and ERP location codes. All entities have *Log audit tables. Dependencies: Prism, Identity.

Catalog (engines/catalog/) - Schema: catalog
Items, item types, and inventory management. Items (tracked assets with type, location, status, quantity, parent-child packing), ItemTypes (schema definitions for item classes), ItemCategories, ItemStatuses with scope/gate validation (status state machines), Tags (RFID EPC/barcodes). Supports import from Excel, movement history, container packing/unpacking. Dependencies: Prism, Spatial, CoreData, SystemEnvironment.

Transport (engines/transport/) - Schema: transport
Shipment and logistics. Shipments (tracking number, type, status, items), ShipmentItemAllocations, Deliveries (multi-destination), receiving operations. Dependencies: Catalog, Spatial.

Processing & Business Logic Engines

BBU (engines/bbu/) - Schema: bbu
BBU-specific business logic for RFID processing. Tag read processing (idempotency tracking), camera read baskets (Datalogic integration), dwell time tracking, door reader checkpoints, movement deduplication, dead letter queue, route statistics. Multiple background processors: MQTT tag reads, Oracle file processing (Azure Blob), shipment lifecycle automation, tag read replay, unknown destination processing, return door processing. Each processor authenticates as a separate service account. Dependencies: all engines.

IoT (engines/iot/) - Schema: iot
IoT device integration and raw data capture. Multiple platform integrations:

  • Zebra FX9600 - RFID reader management
  • Impinj ItemSense - ItemSense platform
  • Teletrac Navman (TN360) - Fleet/vehicle GPS tracking
  • EMQX - MQTT broker connection monitoring
  • EventHub Bridge - Azure Event Hub to RabbitMQ
  • Datalogic Camera - Blob processor for camera XML files
  • Devices, connection events, raw tag reads, management events. Dependencies: Events, Identity.

Events (engines/events/) - Schema: events
CloudEvents-based event publishing system for asynchronous cross-service communication.

Workflow (engines/workflow/) - Schema: workflow
Business process management. Workflow definitions, status transitions, process gates. Status state machine enforcement. Dependencies: CoreData.

Alerts (engines/alerts/) - Schema: alerts
Bulletin/notification system. Bulletins with acknowledgment tracking and comments. Dependencies: Catalog.

Utility & Support Engines

Core-Data (engines/core-data/) - Schema: core
Reference data management (Units of Measure per ISO 80000, currencies, standards) and encryption/decryption services. Standalone, no dependencies.

System-Environment (engines/system-environment/) - Schema: sysenv
System-wide configuration, tenant definitions, environment settings. Dependencies: CoreData.

Printing (engines/printing/) - Schema: printing
Label and document printing. Template-based printing with batch operations, RFID label printing, barcode generation. Uses Dapper for some complex legacy report queries. Dependencies: Catalog, Spatial, Prism, Identity, SystemEnvironment.

File-Handling (engines/file-handling/) - Schema: fs
File upload/download with Azure Blob Storage integration and metadata tracking.

Importer (engines/importer/)
Stateless data import orchestrator for bulk loading. Excel/CSV handling, validation, error reporting. No local database. Dependencies: CoreData.

Intelligence (engines/intelligence/)
Analytics and statistics aggregator. Cross-engine analytics, no local database.

Infrastructure Engines

db-manager (engines/db-manager/)
Centralized database migration orchestration. Runs all engine migrations in dependency order (Prism first) before any service starts. Aspire waits for db-manager completion before launching engines.

AssetTrak UI (engines/assettrak-ui/)
Next.js/React/TypeScript frontend. See The AssetTrak UI.

Doc-Web (engines/doc-web/)
Documentation portal for API reference and architecture docs.


Cortex: The Brain

Cortex (cortex/server/) is a .NET web application providing operational visibility and agent management.

Features

  • Agent Identity Management - Reads .dplx/config.json, tracks "in use" status via sibling worktree existence, displays agent details and active teammates
  • Documentation Browsing - Serves markdown docs, ADR browser, tutorials, reference material
  • Web Dashboard - Overview of active agents

Agent Identity System

24 agent identities defined in .dplx/config.json (Asimov robot names):

  • 6 Team Leads: Bliss, Daneel, Dors, Gaal, Hari (with gender presentation)
  • 18 Non-Leads: Andrew, Ariel, Byerley, Caliban, Dave, Elijah, Elvex, Giskard, Gladia, Hober, Lenny, Nestor, Norby, Powell, Prospero, Robbie, Salvor, Speedy, Susan, Tony
  • 20 Team Names (Asimov planets): Terminus, Trantor, Aurora, Solaria, Gaia, Kalgan, Anacreon, Helicon, etc.

Status tracking:

  • Lead "in use": sibling worktree directory ../acsis-core-{name}-worktree/ exists
  • Teammate active: state file .claude/state/teammates/{name} in lead's worktree
  • Agent name stored in .claude/state/agent_name per worktree

The Passport System (Prism)

The Passport system is the global identity backbone. Every significant entity across all engines gets a globally-unique Passport ID (Guid, UUID v7).

Core Concepts

  • Passport - A globally-unique ID entry. The central registry that says "this ID exists and is of type X"
  • PlatformType (PTID) - Defines what kind of entity a Passport represents (Item=30, Location, Shipment, etc.)
  • Identifier - Maps domain-specific identifiers (barcodes, RFID EPCs, ERP codes) to Passport IDs
  • IdentifierScope - Namespaces for identifiers (a barcode in one system vs another)
  • Attributes - Schema-flexible attribute definitions and value assignments on any Passport
  • Object Hierarchy - Edges and ancestors for parent-child relationships across any entity type

Integration Pattern

All modern engines reference Passports via cross-schema foreign keys:

modelBuilder.AddExternalReference<Passport>()

This gives us:

  • One global ID for any entity, queryable across the entire platform
  • Multiple identifiers (RFID tag, barcode, ERP code) all resolving to the same Passport
  • Flexible attributes attachable to any entity type
  • Hierarchical relationships that span entity types

Migration Philosophy (ADR-034)

  • Modern Dynaplex = Guid primary keys everywhere
  • Legacy AssetTrak = long/int keys (artifacts from old SQL Server)
  • Never write new code to accommodate legacy types
  • Always migrate database toward Guid, never the other way
  • Exception: a handful of reference/core-data entities use int/short for practical efficiency

Database Architecture

Technology

  • PostgreSQL (version 18 for UUID v7 support)
  • Entity Framework Core as the ORM
  • Single database instance, multiple schemas (schema-per-service)

Schema Isolation (ADR-009)

Each engine owns a PostgreSQL schema:

Engine Schema
Prism prism
Identity identity
Spatial spatial
Catalog catalog
Transport transport
IoT iot
BBU bbu
Core-Data core
System-Environment sysenv
Events events
Workflow workflow
Alerts alerts
Printing printing
File-Handling fs

Cross-Schema References

Engines can reference entities in other schemas, but those references are excluded from their own migrations:

modelBuilder.AddExternalReference<Passport>();       // References prism.passports
modelBuilder.ConfigureSchemaIsolation("catalog");    // Must be last call in OnModelCreating

This prevents EF Core from trying to create another schema's tables in your migrations.

DynaplexDbContext Base Class

All engine DbContexts inherit from DynaplexDbContext which provides:

  • SetTenantId(Guid) - Set tenant scope before operations
  • SetSystemScope(reason) - Cross-tenant operations (requires ISystemDbContextUser)
  • SetUserId(Guid) / SetPermissionId(Guid) - Audit context for journal tables
  • ValidateTenantScope() on SaveChanges - Prevents accidental cross-tenant writes
  • PostgreSQL session variables (app.user_id, app.permission_id) for trigger-based journaling

Centralized Migrations (db-manager)

A single db-manager engine runs all migrations in dependency order before any service starts. Aspire uses WaitForCompletion() to block all engines until migrations finish.

Naming Conventions

  • Tables: snake_case (e.g., item_types, location_categories)
  • Columns: snake_case with [Column("column_name")] attributes
  • Journal tables: {table}_journal in the prism schema
  • Migration history: ef_migration_history per schema

API Architecture

Minimal APIs with Static Endpoint Mapping (ADR-013, ADR-015)

Endpoints are defined as static methods in *Api.cs classes:

public static class ItemApi
{
    public static void MapItemEndpoints(RouteGroupBuilder endpoints) { ... }
}

Registered centrally in Program.cs via MapAcsisEndpoints().

Key API Conventions

  • TypedResults for type-safe OpenAPI generation (ADR-014)
  • .WithName() on every endpoint (required for Kiota client generation)
  • .RequireAuthorization() on sensitive endpoints (enforced by analyzer)
  • Endpoint grouping via MapGroup() with tags for OpenAPI organization (ADR-016)
  • OpenAPI metadata enrichment with descriptions and display names (ADR-017)
  • Scalar for API documentation UI (ADR-022, replaced Swagger)
  • RFC 7807 ProblemDetails for error responses
  • FluentValidation for request validation (ADR-024)
  • System.Text.Json standardized serialization (ADR-025)
  • Permission discovery at /.well-known/permissions
  • No API versioning currently (ADR-011 defines strategy for when needed)

Service Orchestration (.NET Aspire)

How It Works (ADR-007)

Each engine runs as its own process. .NET Aspire manages:

  • Service lifecycle and dependencies
  • Dynamic port assignment (never hardcode ports)
  • Health checks for readiness
  • Automatic restart on failure
  • Service discovery for inter-service URLs
  • Azure Container App publishing

AppHost Pattern

The projects/bbu-rfid/ AppHost is the deployable assembly:

var builder = DistributedApplication.CreateBuilder(args);
var dplx = builder.UseDynaplex("bbu");

// Infrastructure
var db = dplx.AddDynaplexDatabase();
var dbManager = dplx.AddDynaplexDbManager();

// Engines registered with metadata and dependencies
var prism = builder.AddEngine<PrismProject>(metadata);
var catalog = builder.AddEngine<CatalogProject>(metadata)
    .WithDependency(prism)
    .WithDependency(spatial);

Key Infrastructure

  • PostgreSQL - Shared database with per-schema isolation
  • Azure App Configuration - Centralized config with Key Vault secrets
  • Application Insights - Monitoring and telemetry
  • Azure Storage - Blob storage for files and imports
  • Apache Superset - Analytics dashboards with Redis cache
  • EMQX - MQTT broker for IoT device communication
  • RabbitMQ - Message queue for event bridges

Dependency Management

  • WaitFor() chains engine startup order
  • WaitForCompletion() for db-manager (waits for exit, not just Running state)
  • Engine dependencies declared via DependencyManifest and wired automatically

Inter-Service Communication (Kiota)

Microsoft Kiota (ADR-021)

Kiota generates strongly-typed HTTP clients from OpenAPI specifications at build time.

How It Works

  1. Each engine exposes an OpenAPI spec
  2. Kiota generates a {Engine}ApiClient project from that spec
  3. Other engines reference the ApiClient project for type-safe calls
  4. Service discovery resolves URLs at runtime

Registration Pattern

builder.Services.AddAcsisApiClient<CatalogApiClient>();

This auto-discovers:

  • CatalogApiClientFactory (implements IAcsisApiClientFactory<CatalogApiClient>)
  • Handler attachment extension methods
  • Named HttpClient with service discovery URL
  • Standard resilience handler (retry, circuit breaker, timeout)

Dependency Chain

Catalog    → Prism, Spatial, CoreData, SystemEnvironment
Transport  → Catalog, Spatial
Spatial    → Prism, Identity
Identity   → CoreData, Spatial, SystemEnvironment
BBU        → All engines (comprehensive integration)
Printing   → Catalog, Spatial, Prism, Identity, SystemEnvironment
IoT        → Events, Identity

Authentication & Authorization

JWT Bearer Authentication (ADR-010)

  • Identity engine issues JWT tokens with RSA key signing
  • Three validation modes: RSA public key, Authority-based discovery, Service discovery
  • Default issuer: acsis-identity, audience: acsis-api
  • 5-minute clock skew tolerance

Authorization Policies (ADR-028)

  • CanManageItems: Advanced User, Supervisor, SystemAdmin, SuperUser
  • CanDeleteItems: SystemAdmin, SuperUser only
  • RequireTenant: Requires tenant context
  • SystemAdministration: SystemAdmin or SuperUser
  • Permission graph with hierarchical expansion (background sync service)
  • Permission discovery endpoint: /.well-known/permissions

Service-to-Service Auth

  • Header propagation forwards Authorization tokens between services
  • BBU processors each authenticate as separate service accounts
  • NullSafeHeaderPropagationHandler handles missing correlation IDs

Observability & Operations

OpenTelemetry (ADR-019)

  • Distributed tracing across all services
  • Metrics collection
  • Structured logging with correlation IDs
  • Export to OTLP or Azure Monitor
  • Health check endpoints excluded from tracing

Structured Logging (ADR-026)

  • Serilog with daily rolling file output
  • Scoped properties and correlation IDs
  • Console and file sinks

Health Checks

  • /health - Readiness (checks dependencies)
  • /alive - Liveness (is the process running)
  • Aspire dashboard shows all service health status

Aspire Dashboard

  • Real-time service status and health
  • Distributed traces viewer
  • Structured log viewer
  • Metric graphs
  • Dynamic endpoint discovery (use this, never hardcode ports)

Build System & Developer Experience

Solution Format

acsis-core.slnx - XML-based solution format (ADR-005)

Central Package Management (ADR-050)

Directory.Packages.props in the repository root manages all NuGet package versions centrally. Packages organized alphabetically. Current stack: .NET 10, Aspire, EF Core, Kiota, Shouldly, xUnit, NSubstitute, etc.

MSBuild Properties (ADR-051)

Directory.Build.props defines:

  • Path properties: $(RootDir), $(EngineDir), $(ProjDir), $(StrataDir), $(CortexDir)
  • Component references as MSBuild properties - prevents long relative paths
  • Auto-detection of project types: $(IsTests), $(IsAbstractions), $(IsApiClient), $(IsDatabase), etc.
  • Automatic analyzer attachment to Database projects
  • Source generator attachment to Database projects

Project Templates

dotnet new templates for scaffolding complete engine projects with proper namespaces, schema constants, project references, and analyzer configuration.

Testing Framework

  • xUnit for test execution
  • Shouldly for assertions
  • NSubstitute for mocking
  • Testcontainers for database testing (PostgreSQL)
  • TngTech.ArchUnitNET for architecture tests
  • Playwright for E2E testing (AssetTrak UI)
  • Separate .UnitTests and .IntegrationTests projects per engine

Architectural Enforcement (Analyzers)

Overview (ADR-004)

31 Roslyn analyzer classes produce 88 diagnostic rules (ACSIS0001-ACSIS0087) that enforce Dynaplex patterns at compile time.

Rule Categories

Architecture & Boundary Enforcement

  • Strata purity - Strata can't reference engines, EF Core, or ASP.NET Core (ADR-046)
  • Abstractions can't reference Database projects (ADR-036)
  • No cross-component DbContext injection
  • Forbidden namespace references

Database & Entity Patterns

  • Schema isolation enforcement - prevents cross-schema table creation in migrations
  • Journal table validation - must inherit JournalTable, use prism schema
  • Passport entity patterns - detects unsafe creation, missing DatabaseGenerated attributes
  • PostgreSQL snake_case column naming convention
  • DbContext architecture rules (SCHEMA constants, location)

API & Service Patterns

  • TypedResults required for type-safe OpenAPI
  • .WithName() required on all endpoints (for Kiota generation)
  • .RequireAuthorization() required on sensitive endpoints

Configuration & DI

  • Scoped-in-singleton detection
  • Duplicate CORS/auth configuration prevention
  • Options validation at startup

Code Quality

  • No async void methods
  • Use DateTime not DateTimeOffset (PostgreSQL UTC)
  • Test naming conventions
  • XML documentation on public APIs

Suppression

Rules can be suppressed with [ADR036Exception("justification")] or [SuppressMessage] when architecturally appropriate.


Source Generation

JournalEntityGenerator

Generates journal (audit) entity classes at compile time:

  • Triggered by [JournalFor(typeof(SourceEntity))] on partial classes
  • Copies public read/write properties from source entity
  • Preserves attribute annotations ([Column], [Required], [StringLength], [Precision])
  • Skips journal-specific and navigation properties
  • Output in Generated/ folders

EngineIndexGenerator

Creates a strongly-typed EngineIndex from EngineManifest.cs files. Used for service discovery and metadata-driven configuration. Detects duplicate engine name registrations.

AuthenticatedProcessorGenerator

Generates wrapper code for message processors that require authentication context before processing.


Journal & Audit System

Design (ADR-035)

Every significant entity has a companion journal table that records all state changes.

How It Works

  1. Journal tables live in prism schema regardless of source entity's schema
  2. Named {table}_journal (e.g., prism.items_journal)
  3. Composite primary key: (Id, SeqId) where Id is the source entity's PK and SeqId is auto-incremented
  4. PostgreSQL triggers automatically write journal entries on INSERT, UPDATE, DELETE
  5. A generic prism.journal_writer() trigger function handles all source tables
  6. User/Permission context set via PostgreSQL session variables (app.user_id, app.permission_id) before SaveChanges
  7. Tracks ValidFrom, ValidTo, InitiatedByUserId, InvalidatedByUserId, InitiatedByPermissionId

Entity Pattern

[JournalFor(typeof(Item))]                              // Source generation trigger
[Table("items_journal", Schema = "prism")]              // Always prism schema
public partial class ItemLog : JournalTable
{
    public const string TABLE_NAME = "items_journal";
    // Properties auto-generated from Item entity
}

Multi-Tenancy

Design (ADR-037)

Multi-tenancy is an explicit, first-class architectural concern.

How It Works

  1. HttpContextTenantResolver extracts tenant ID from JWT claims in the HTTP context
  2. SetTenantId(Guid) is called after the DbContext factory creates a context instance
  3. All tenant-scoped entities have a TenantId column (indexed for performance)
  4. ValidateTenantScope() runs on SaveChanges to prevent cross-tenant writes
  5. System scope (SetSystemScope(reason)) allows cross-tenant operations with explicit justification and requires ISystemDbContextUser marker interface
  6. Default tenant service auto-detects single tenant for early development scenarios

Tenant Scoping Modes

  • RequiresSetup - Tenant must be set before any operation
  • TenantScoped - Normal operation within a tenant
  • SystemScope - Explicitly bypasses tenant filtering (admin operations)

The AssetTrak UI

Technology

Next.js / React / TypeScript application (engines/assettrak-ui/)

Architecture (ADR-039)

  • Server-side proxy pattern - Next.js API routes proxy backend service calls
  • API base URLs injected as environment variables by Aspire's AddDynaplexNextJsApp()
  • Auto-discovery scans EngineIndex to build service URLs

Pages

Page Purpose
/assets/ Item/asset management, search, bulk operations
/item-types/ Item type management and creation
/item-categories/ Category management
/locations/ Location hierarchy browser
/location-categories/ Location category management
/regions/ Region management
/shipments/ Shipment tracking and management
/receive/ Shipment receiving interface
/unpack/ Container unpacking workflow
/workflows/ Workflow management
/statuses/ Status configuration
/alerts/ Bulletin system
/reports/ Reporting
/print-labels/ Label printing
/login/ Authentication
/profile/ User profile

Features

  • Real-time item search
  • Bulk operations
  • RFID tag management
  • Container packing/unpacking workflows
  • Import/export functionality
  • Permission-based UI rendering
  • E2E tests via Playwright with centralized selectors.ts and data-testid attributes

Agent Infrastructure

Session Management

  • dplx session new [agent] - Creates sibling worktree (../acsis-core-{name}-worktree/), restores packages, configures git identity, assigns random team name, launches Claude Code with Agent Teams
  • dplx session clean [agent] [--force] - Kills processes, removes containers/volumes, checks for uncommitted work, removes worktree, deletes branch if merged
  • dplx session list [--json] - Shows active sessions and available leads

Agent Teams (ADR-055)

Replaced the custom Cortex messaging/reservation system with Claude Code native Agent Teams:

  • Lead agents spawn teammates who work within the lead's worktree
  • Teammates claim non-lead identities via state files
  • File-based identity management instead of 12 database tables + 21 MCP tools
  • Task coordination through the native Agent Teams task system

Linear Issue Tracking

Issue tracking via Linear (accessed through Linear MCP server tools):

  • Dependency-aware task management
  • Priority tracking (P0-P4)
  • Cross-session persistence
  • Referenced in commit messages (Refs: SWE-xxx)

Git Workflow (ADR-053)

  • Per-agent branches: agent/{name}
  • Worktrees are sibling directories sharing .git but not build output
  • Direct merge to main (no PRs during rapid development phase):
    git fetch origin main && git merge origin/main && git push origin agent/{name}:main
    
  • Conventional Commits with AI co-authorship (ADR-054)

Deployment & Cloud Infrastructure

Current Deployment

BBU-RFID Project deployed to Azure for development and testing.

Azure Resources (ADR-043)

  • Azure Container Apps - Engine hosting
  • Azure Container App Environment - Shared infrastructure
  • PostgreSQL Flexible Server - Database (version 18)
  • Azure App Configuration - Centralized configuration with Key Vault
  • Application Insights - Monitoring and telemetry
  • Azure Blob Storage - File storage
  • Azure Event Hub - IoT event ingestion
  • Provisioned via Bicep files and Azure Developer CLI (azd)

Network Architecture (ADR-044, ADR-045)

  • PostgreSQL public access configuration
  • VNet service endpoints for Azure networking

Container Configuration

  • Default: 1 CPU, 2 GB RAM per container
  • Azure Container App publishing via Aspire

ADR Index

Complete list of Architecture Decision Records for reference:

# Title Status
001 Polylith Architecture Adoption Active
002 Spec Naming Convention Superseded
003 Dynamic Component Loading Superseded by 007
004 Roslyn Analyzer Enforcement Active
005 .slnx Solution Format Active
006 Directory.Build.props Centralization Active
007 Aspire Microservices Migration Active
008 .NET 9 Adoption Superseded by 029
009 Database Schema per Service Active
010 JWT Bearer Authentication Active
011 API Versioning Strategy Active
012 Phasing Out Enterprise Library Active
013 Minimal APIs with Static Classes Active
014 TypedResults for Type-Safe Responses Active
015 Extension Methods for Endpoint Registration Active
016 Endpoint Grouping and Tagging Active
017 OpenAPI Metadata Enrichment Active
018 Removing MediatR/CQRS Active
019 OpenTelemetry Integration Active
020 Container-Aware Configuration Active
021 Microsoft Kiota API Client Generation Active
022 Scalar API Documentation Active
023 Component Service Registration Active
024 FluentValidation Strategy Active
025 System.Text.Json Standardization Active
026 Structured Logging and Observability Active
027 Event-Driven Architecture Active
028 RBAC Migration Active
029 .NET 10 LTS Migration Active
030 Distributed Caching Strategy Active
031 API Gateway Pattern Active
032 Unit of Measure System Active
033 Base Orchestration Pattern Active
034 GUID Primary Keys (UUID v7) Active
035 Prism Journal Tables Active
036 Database Project Separation Active
037 Explicit Tenant Scope Operations Active
038 Permission Discovery Service References Active
039 Next.js API Proxy Pattern Active
040 HTML-First UI Architecture Active
041 BBU Processing Hardening Active
042 MQTT Message Delivery Architecture Active
043 Azure Infrastructure Architecture Active
044 PostgreSQL Public Access Active
045 VNet Service Endpoints Active
046 Library Projects (Strata Purity) Active
047 Static Message ID Strategy Active
048 Agent-Driven Local Dev Stack Proposed
049 Stream-Centric Zebra RFID Architecture Active
050 Central Package Management Active
051 MSBuild Component Reference Properties Active
052 Semantic Versioning with Nerdbank.GitVersioning Proposed
053 Git Workflow Guidelines Active
054 Commit Message Guidelines Active
055 Agent Teams Integration Active
056 E2E Test Infrastructure Conventions Active

Evolution Highlights

This section traces the key architectural evolution decisions:

  1. Dynamic loading → Microservices (ADR-003 → ADR-007): Moved from Assembly Load Context plugin pattern to Aspire-orchestrated process isolation. Cloud-native won.

  2. MediatR/CQRS → Direct handlers (ADR-018): Removed unnecessary indirection. Simpler is better.

  3. Swagger → Scalar (ADR-022): Modern API documentation UI.

  4. SQL Server → PostgreSQL: Legacy AssetTrak was SQL Server. Modern Dynaplex is PostgreSQL with UUID v7, schema isolation, and trigger-based journaling.

  5. long/int → Guid (ADR-034): The most impactful migration decision. Every entity gets a globally-unique Passport ID.

  6. Custom agent messaging → Agent Teams (ADR-055): Replaced 12 database tables and 21 MCP tools with native Claude Code orchestration and filesystem state tracking.

  7. .NET 9 → .NET 10 (ADR-008 → ADR-029): Staying on the latest LTS.

  8. Enterprise Library → Modern patterns (ADR-012): Out with the old, in with the standard ASP.NET Core middleware.


This document represents the state of the Dynaplex platform as of February 2026. It is generated from direct codebase exploration and reflects what has been built, not aspirational goals.