Enterprise systems often accumulate complexity in one of two ways. A monolith places everything in one assembly until every change affects unrelated behavior. A micro-fragmented design produces dozens of packages with unclear ownership, duplicated infrastructure, and circular dependencies.
ITS Framework takes a third path: a fixed number of layers, each with a clear contract and a defined reason to exist. Cross-cutting concerns are solved once in the lowest layer that needs them. Business modules are discovered at runtime and depend on the platform, never on one another.
The ITS Framework desktop runtime targets .NET 10 for Windows (net10.0-windows). That target applies to the WinForms host and framework assemblies described here. ITSV Web is a separate compatibility surface that remains on ASP.NET Web Forms and .NET Framework 4.8.
The framework was designed specifically for remote users who connect to an application server in the cloud. The user receives a complete desktop application experience while the database, shared caches, modules, and background services remain close together inside the cloud environment. Only the remote display and interaction cross the user connection.
Why a Layered Component Architecture
The goal is not layering for its own sake. The goal is a predictable place for every capability and a predictable change surface when the business evolves.
- Components can be replaced or extended without reaching around their contracts.
- Applications are assembled by composition rather than deep inheritance.
- Business modules can be loaded without the shell knowing them at build time.
- Caching, security, validation, messages, DPI, and event coordination are implemented once and reused above.
Adding a business capability should usually mean adding configuration and, only when necessary, one small assembly—not modifying the framework.
The Layered Map
L7 BUSINESS APP MODULES
Pluggable line-of-business capabilities loaded on demand
↑
L6 ADVANCED UI FRAMEWORK
Composite screens, dashboards, pivots, and wizards
↑
L5 DATA-DRIVEN UI CORE
Dynamic grids, editors, and recursive form composition
↑
L4 UI HELPERS + DATA BRIDGE
DPI, themes, binding, control extensions, and adapters
↑
L3 DATA ACCESS LAYER
Named datasets, SQL parameter chain, and security context
↑
L2 CROSS-CUTTING SERVICES
Icons, email, PDF, AI, crypto, hardware, and WMI
↑
L1 FOUNDATION
Types, extensions, caches, messages, validation, mini-DSLs
↑
L0 HOST PROCESS
The native executable that boots and contains the runtime
The arrows describe dependency direction: a layer can build on contracts below it, but lower layers cannot know about components above them.
Designed for Remote Cloud Sessions
ITS Framework is designed to run on centralized cloud application servers and to be consumed through remote desktop sessions. A remote user signs in and sees AppLauncher, the framework shell. From that single surface, the user can open the modules permitted by the security context, move among them, monitor their state, and close them when the work is complete.
This operating model is intentionally resource-efficient. A module is not another full application process with another configuration graph, connection pool, icon catalog, security context, and set of decoded files. Modules reuse the expensive objects already owned by the session runtime.
| Per remote session | Shared across its modules |
|---|---|
AppLauncher shell and module windows | DAL and named dataset cache |
| Module-specific UI state | Security and language context |
| One execution thread per active module | File, image, icon, and configuration caches |
| Module cancellation and lifecycle state | Message Codes, task manager, and telemetry |
Each active module runs on its own managed thread and owns the message loop and transient state required by its forms. That prevents a long-running or unresponsive module from automatically stopping the interaction thread of every other module. At the same time, all module threads can safely reach the same concurrent caches, DAL contracts, configuration objects, and security context.
Remote user
↓ RDP / remote application session
AppLauncher
+-- Module A thread ------------------+
+-- Module B thread ------------------+-- Shared DAL, caches,
+-- Module C thread ------------------+ security, configuration
+-- SQL Server and storage
AppLauncher as the module supervisor
AppLauncher is more than a menu. It is the session-level supervisor. It knows which modules are open, which threads own them, what background work they submitted, and whether they are responding. The user can launch a module, switch to it, request that it close, or cancel its pending tasks from one place—similar to the operational visibility provided by Windows Task Manager, but at the application-module level.
Module shutdown is cooperative inside the shared process: the launcher requests cancellation, closes the module forms, releases subscriptions, and allows the thread to exit cleanly. This protects the common objects used by other modules. When a capability runs behind a separate process boundary, the supervisor can terminate and restart that process if it stops responding.
The architecture minimizes resources by sharing infrastructure, while module-owned threads and supervised lifecycle boundaries keep one module's work from becoming every module's problem.
Host, Foundation, and Services
L0: The host process
The host is the single executable for a deployment role. It validates the installation, initializes security, performs the startup call that hydrates configuration, loads the shell, and owns native resources that dynamically loaded assemblies cannot carry independently.
The host does not contain business knowledge. It reads the module registry and delegates.
L1: Foundation
The foundation supplies runtime primitives without UI, database, or external-service dependencies:
- Generic caches with strong heavy-object references, weak wrappers, and adaptive maintenance.
- File and image caches plus typed TTL caches.
- Message Codes that combine validation, multilingual messages, dialog semantics, and flow control.
- Mini-DSLs such as
TParsandSQLPMetas. - Reader-writer locks, calibrated event debounce, and expression-driven validation.
The foundation is deliberately isolated so its mechanisms can be tested without booting a database or a visual shell.
L2: Cross-cutting services
This layer packages capabilities needed by many consumers but not essential to the core runtime. Icon lookup, email, PDF, AI, cryptography, machine information, themes, processes, services, and RDS session information remain independent services. A deployment can omit one capability without changing the rest of the architecture.
L3: The Contract with Data
The DAL is the authoritative database boundary. Consumers ask for logical datasets such as ClaimDetails; they do not combine connection strings, SQL text, and mapping rules.
This naming contract separates what a consumer needs from how the dataset is produced. The DAL can execute a stored procedure, return a cached prototype clone, or load a serialized snapshot without changing the caller.
The parameter resolution chain
Queries need context from several owners: identity from security, state from the module, filters from the composite view, and selection from a grid. Each contributor implements the same small contract, adds only the parameters it owns, and satisfies entries in SqlParList.
Security context → user, language, tenant
Module controller → module state
Composite view → active filters
Grid selection → current entity keys
↓
Complete SQL parameter set
No component must understand the complete request, yet the stored procedure receives the complete context.
One boundary for cross-cutting data behavior
Because reads and writes pass through the DAL, connection pooling, retries, transactions, telemetry, error translation, structured UDTT writes, caching, and security enforcement have one home. Business modules do not recreate them.
Extensions: The Domain Dialect
Across the formal layers is an informal productivity layer: extension methods that make .NET types speak the language of the framework.
| Base type | Framework vocabulary |
|---|---|
DataRow | Typed access, safe nulls, state helpers, validation, projections |
string | Metadata parsing, token resolution, SQL descriptors, multilingual messages |
Dictionary<string,string> | Defaults, coercion, merge, diff, serialization |
Image, FileInfo, Control | DPI-aware imaging, backup-safe I/O, control traversal |
int planId = row.G<int>("plan_id");
string path = meta.T("PATH");
Image thumb = img.ResizeToH(64);
IEnumerable<CntrlG> grids = ctrl.Descendants<CntrlG>();
The result is a domain-specific language built on familiar .NET types. A new DataRow extension immediately benefits every grid, editor, and module without additional plumbing.
Configuration as the Program
Screens, menus, grids, columns, styles, validations, commands, modules, reports, and permissions are structured data. That makes the configuration database replaceable by any source that can supply the same named datasets.
- A live SQL Server schema edited by administrators.
- A serialized
DataSetshipped beside the application. - An embedded resource, service download, or build-pipeline output.
This enables database-free demonstrations, deterministic tests, frozen releases, tenant-specific behavior, configuration diffing, and portable application definitions. The DAL hides the source, the caches keep the result warm, and the UI consumes the same rows regardless of origin.
The behavior of an application becomes a data value that can be saved, compared, shipped, restored, and activated.
L4–L6: The Three UI Layers
L4: UI helpers and data bridge
L4 adapts the runtime and DAL to visual controls. It owns control-tree traversal, DPI scaling, image handling, dark-mode helpers, binding sources, and dataset-to-control adapters. This boundary keeps WinForms and WPF concerns out of the foundation and DAL.
L5: Data-driven UI core
L5 turns metadata into working controls at runtime. Its primitives include dynamic grids, row property editors, composite views, recursive group composition, and cross-filtering controllers. Columns, editors, validation rules, styles, master-detail relationships, and commands come from configuration rather than designer code.
L6: Advanced UI framework
L6 combines L5 primitives into complete editor screens, dashboards, pivots, cross-tabs, and wizards. It should introduce combinations, not new primitives; genuinely reusable primitives belong in L5.
L7: Open-Ended Business Modules
Each business module is an independent assembly exposing one or more top-level forms. The configuration registry identifies the assembly, type, title, size, icon, ordering weight, and module-specific metadata.
The shell loads modules through reflection. Adding one means deploying an assembly and inserting a registry row. Modules may depend on L1–L6, but never on peer business modules. Shared concerns move downward into a reusable layer or are coordinated through shared data.
The same registry can represent external processes such as browsers, editors, and third-party viewers, allowing the shell to treat internal and external capabilities through one launch model.
The Built-In Module Catalog
The platform begins with general-purpose production modules rather than an empty shell:
- File Archive: versioned storage, previews, search, tagging, email ingestion, and attachments.
- Security: users, roles, groups, module and command permissions, row filters, sessions, and audit.
- Entity Manager: metadata-driven master-data editing for people, facilities, providers, vendors, plans, and catalogs.
- Form Editor: visual administration of the configuration tables that define other screens.
- User Support Dashboard: caches, sessions, module instances, task queues, errors, and message diagnostics.
- File Parser / Interchange: metadata-defined fixed-width, delimited, XML, JSON, and EDI-style intake.
- Application Launcher: MDI shell, menu, registry, session state, layout persistence, and coordination.
Domain packs add pharmacy, claims, plan design, member policy, inventory, and health-library capabilities through the same L7 contract.
One Shared Runtime
All open modules in a user session share one host process, DAL surface, configuration cache, file and image caches, security context, and Message Code catalog. A dataset fetched by one module can be reused by another; a file opened in the archive is already warm when a claims view requests it.
Each module owns a dedicated UI thread, while SQL calls, file I/O, PDF rendering, mail parsing, AI requests, reports, and refreshes can use additional worker tasks coordinated through the framework task manager. Results return to the owning module thread through controlled dispatch and debounced events. Foundation locks and concurrent dictionaries protect the common state reached by all module threads.
The built-in task manager
Modules submit background work to one observable task manager instead of creating unmanaged threads. It schedules jobs, reports progress, supports cancellation and retries, and exposes state through the support dashboard. Cache maintenance, prefetching, mail polling, archive scans, and interchange intake use the same infrastructure.
When isolation is necessary, the process supervisor can launch external modules or server workers as separate processes. The architecture is multi-threaded inside the host and multi-process at selected boundaries.
Adding a New Module
Scenario A: Master data with no C#
- Define or reuse the underlying database table.
- Register its logical dataset and source.
- Add column metadata for labels, editors, validation, formats, and permissions.
- Register the generic Entity Manager with the entity metadata key.
- Add the menu row.
The generic module materializes grid, editor, filters, audit, and permissions from those rows.
Scenario B: A composed screen with no C#
Define panels, docking, datasets, commands, and cross-filter rules in the composer tables, then register the built-in composer host. The recursive composer builds the screen when it opens.
Scenario C: New behavior in a small assembly
When the requirement is a workflow, external integration, or domain calculation that metadata cannot express, create a focused L7 handler referencing only platform layers. Grids, security, persistence, caching, tasks, and diagnostics remain framework responsibilities.
Dependency Rules
- Dependencies move upward through the stack. The DAL cannot know about visual controls.
- Layers do not reach around ownership boundaries. Shared functionality is exposed through an appropriate contract.
- The UI bridge is where data meets presentation. Controls never construct SQL.
- Business modules are peers. They coordinate through data and shared platform services, not direct references.
- Native and host-only concerns remain in L0.
These rules matter more than the number of projects. They prevent convenience dependencies from turning a component catalog into a circular graph.
How an Application Composes Itself
Host boots
→ security context initializes
→ DAL loads configuration in one startup contract
→ shell reads the module registry
→ user opens a module
→ reflection loads the registered assembly and form
→ UI core composes controls from metadata
→ parameter contributors assemble dataset context
→ DAL supplies named datasets
→ shared caches serve files, images, icons, and references
→ task manager coordinates background work
This runtime story is not business-module-specific. Every application built on the framework follows the same composition path.
Boundaries and Tradeoffs
This architecture fits a controlled enterprise runtime in which remote users connect to cloud application servers, modules execute near SQL Server, share a trusted process boundary, and are governed by one platform team. Its strengths depend on disciplined metadata, stable layer ownership, and observability of dynamic loading.
- Shared process state and module-owned UI threads reduce duplication but require strict thread safety, controlled dispatch, cooperative cancellation, and lifecycle management.
- Reflection and metadata reduce compile-time coupling but move some failures to startup or module-open time.
- A database-centered contract is effective for this RDS/WinForms environment; it is not automatically the right boundary for public APIs, offline clients, or untrusted networks.
- A serialized configuration snapshot is portable, but release governance must prevent stale or incompatible schemas.
- Layer rules require architectural enforcement. Project names alone cannot prevent dependency erosion.
Reading the Framework as a Component Catalog
- L0 is the container.
- L1 is the language.
- L2 is the toolkit.
- L3 is the contract with data.
- Extensions are the domain dialect.
- L4 is the translator.
- L5 compiles metadata into UI.
- L6 is the library of complete visual compositions.
- L7 is where the business lives.
The configuration database is the program expressed as data. When deciding where a capability belongs, ask for the lowest layer that already needs to understand it. Put it there, expose a stable contract, and let every layer above compose it.