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-corerepository.
Table of Contents
- What is Dynaplex?
- Repository Structure: The Three Layers
- Strata: The Foundation
- Engines: The Computational Cores
- Cortex: The Brain
- The Passport System (Prism)
- Database Architecture
- API Architecture
- Service Orchestration (.NET Aspire)
- Inter-Service Communication (Kiota)
- Authentication & Authorization
- Observability & Operations
- Build System & Developer Experience
- Architectural Enforcement (Analyzers)
- Source Generation
- Journal & Audit System
- Multi-Tenancy
- The AssetTrak UI
- Agent Infrastructure
- Deployment & Cloud Infrastructure
- 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 onSaveChangesDatabaseExtensions- Cross-schema entity references (AddExternalReference<T>), schema isolation configuration, Passport relationship wiring, PostgreSQL snake_case namingJournalTable- Base class for all audit log entities with composite key (Id, SeqId), validity tracking, and user attributionComponentSchemaRegistry- Runtime schema discovery via reflection; prevents migrations from recreating tables in wrong schemasDependencyManifest- Fluent declaration of engine-to-engine dependenciesITenantResolver/HttpContextTenantResolver- Tenant ID extraction from auth claimsScopedDbContextFactory- Creates DbContext instances with proper tenant contextIReferenceDataSeeder- 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 trackingAddDynaplexDatabase()- PostgreSQL with tenant isolationAddDynaplexAppConfiguration()- Azure App Config with Key VaultAddDynaplexAppInsights()- Application InsightsAddDynaplexStorage()- Azure Storage accountsAddDynaplexAnalytics()- Apache Superset + RedisAddDynaplexNextJsApp()- 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_nameper 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/intkeys (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/shortfor 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 operationsSetSystemScope(reason)- Cross-tenant operations (requiresISystemDbContextUser)SetUserId(Guid)/SetPermissionId(Guid)- Audit context for journal tablesValidateTenantScope()onSaveChanges- 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}_journalin theprismschema - Migration history:
ef_migration_historyper 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 orderWaitForCompletion()for db-manager (waits for exit, not just Running state)- Engine dependencies declared via
DependencyManifestand 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
- Each engine exposes an OpenAPI spec
- Kiota generates a
{Engine}ApiClientproject from that spec - Other engines reference the ApiClient project for type-safe calls
- Service discovery resolves URLs at runtime
Registration Pattern
builder.Services.AddAcsisApiClient<CatalogApiClient>();
This auto-discovers:
CatalogApiClientFactory(implementsIAcsisApiClientFactory<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
NullSafeHeaderPropagationHandlerhandles 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
.UnitTestsand.IntegrationTestsprojects 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, useprismschema - Passport entity patterns - detects unsafe creation, missing
DatabaseGeneratedattributes - 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
DateTimenotDateTimeOffset(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
- Journal tables live in
prismschema regardless of source entity's schema - Named
{table}_journal(e.g.,prism.items_journal) - Composite primary key:
(Id, SeqId)whereIdis the source entity's PK andSeqIdis auto-incremented - PostgreSQL triggers automatically write journal entries on INSERT, UPDATE, DELETE
- A generic
prism.journal_writer()trigger function handles all source tables - User/Permission context set via PostgreSQL session variables (
app.user_id,app.permission_id) beforeSaveChanges - 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
HttpContextTenantResolverextracts tenant ID from JWT claims in the HTTP contextSetTenantId(Guid)is called after the DbContext factory creates a context instance- All tenant-scoped entities have a
TenantIdcolumn (indexed for performance) ValidateTenantScope()runs onSaveChangesto prevent cross-tenant writes- System scope (
SetSystemScope(reason)) allows cross-tenant operations with explicit justification and requiresISystemDbContextUsermarker interface - Default tenant service auto-detects single tenant for early development scenarios
Tenant Scoping Modes
RequiresSetup- Tenant must be set before any operationTenantScoped- Normal operation within a tenantSystemScope- 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.tsanddata-testidattributes
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 Teamsdplx session clean [agent] [--force]- Kills processes, removes containers/volumes, checks for uncommitted work, removes worktree, deletes branch if mergeddplx 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
.gitbut 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:
Dynamic loading → Microservices (ADR-003 → ADR-007): Moved from Assembly Load Context plugin pattern to Aspire-orchestrated process isolation. Cloud-native won.
MediatR/CQRS → Direct handlers (ADR-018): Removed unnecessary indirection. Simpler is better.
Swagger → Scalar (ADR-022): Modern API documentation UI.
SQL Server → PostgreSQL: Legacy AssetTrak was SQL Server. Modern Dynaplex is PostgreSQL with UUID v7, schema isolation, and trigger-based journaling.
long/int → Guid (ADR-034): The most impactful migration decision. Every entity gets a globally-unique Passport ID.
Custom agent messaging → Agent Teams (ADR-055): Replaced 12 database tables and 21 MCP tools with native Claude Code orchestration and filesystem state tracking.
.NET 9 → .NET 10 (ADR-008 → ADR-029): Staying on the latest LTS.
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.