Hello everyone! Today I want to share a real-world development journey where I served as both solution architect and developer. My challenge? Creating a dynamic UI system for an enterprise application with user-configurable fields.
The business context
This application emerged during the COVID period as a sophisticated alternative to conventional video calling tools like Teams or Zoom. Beyond simple video appointments, the platform integrated several critical business components:
- Customer management
- Document handling with digital signature capabilities
- Contract management
- Configurable workflows to guide users through multi-step processes
The system needed to support diverse business scenarios:
- HR Teams used it to manage candidate information, schedule interviews, collect documents, and handle digital contract signing.
- FinancIAL Services leveraged it to engage potential clients, plan virtual meetings, and facilitate document collection with secure contract signing..
- Medical Device Companies utilized it for patient support, allowing staff to schedule immediate or future video appointments to provide technical assistance.
These varied use cases demanded a flexible architecture where entity fields could adapt to each client’s unique requirements without custom coding for every installation.
Key objectives
- Create a dynamic property management system that clients could configure through an admin interface.
- Design a resilient system capable of handling diverse use cases and custom client requirements.
- SOLID principles to ensure the platform can evolve gracefully over time.
Technical Architecture
The application used a .NET Core backend with an Angular frontend.
Our backend architecture featured a central modular monolith surrounded by microservices shared across other company applications. The core consisted of two primary modules: contracts and appointments.

The automated release followed this scheme:

Requirements analysis
I developed this software for several years, during which modules and new features were added. I’ll describe here a summary of what I eventually developed.
The first phase was an analysis of what I would need to manage a dynamic field, identifying the characteristics that describe it. A list could be as follows:
- Field type (string, date, numeric, email, phone, etc.)
- Label
- Display order Group (properties with the same group name were grouped on the UI side)
- Readonly (useful for managing calculated fields)
- Entity it belongs to (personal records, contract, appointment, etc.)
- Required
- Visible in list (with this flag, I could manage the creation of lists dynamically, choosing which fields to show. A field visible in a list was automatically also filterable)
- Unique (software constraint; it was checked that the unique value was unique in the entire table, e.g., tax code in personal records)
- Quick creation (e.g., when adding a new customer, a modal opened with the required fields and quick creation fields, to enter only the main data and postpone a more complete customer profiling to a later time)
In addition, there was also a concept of visibility and field management based on the user’s role. I had planned that a role corresponded to a read/write/modify/delete permission for each entity; these permissions were then inherited by the display mask in order to allow or not allow operations on the data. Obviously, there was also a backend control to manage security at this level.
Architecture
Database implementation
I created an entity configuration table to track all dynamic fields and their characteristics. Each entity type (customers, appointments, contracts) had its own base table that could be extended through this system.
Backend architecture
When an administrator added a new field through the UI, the system automatically created a corresponding database column. This approach allowed us to connect any entity table to the company’s existing reporting tools.
Rather than defining static classes with fixed properties, I designed a base class that all dynamic entities inherited from. This base class contained a collection of property objects representing both the field definitions and their values. This unified approach worked consistently for both new and existing database records.
To optimize performance, I cached frequently accessed data structures to minimize database calls.
Frontend implementation
The frontend received objects with a similar structure to the backend model, but with inaccessible properties filtered out. Each property contained all the rendering information needed by its corresponding Angular component. These components were grouped within Angular reactive forms that managed data entry and updates.
Infrastructure
Unfortunately, the company where I developed this application had an outdated and poorly updated infrastructure. The application ran inside IIS (😭), excluding the possibility of scaling according to load.
Building resilience through modular customization
The true test of our architecture came when clients requested customizations to standard processes. One client, for example, wanted to display calculated values in read-only fields.
I faced a critical architectural choice:

Option 1: Embed customizations in the core code
PRO
- Faster initial implementation
CON
- Creates technical debt
- Increases complexity over time
- Risk of breaking the dynamic workflow system
Option 2: Create a modular customization system
PRO
- Keeps core code clean
- Isolates client-specific logic
- Prevents customization conflicts
I chose the modular approach (Option 2). First, I identified extension points in the application lifecycle: before/after the entity saves, during validation, within scheduled tasks, etc. Then, I defined interfaces exposing methods that could be extended for custom functionality.
Each client customization lived in its module that implemented these interfaces with client-specific logic. The appropriate modules were loaded via dependency injection based on the installation configuration. This system even supported automated jobs, which we used to synchronize data between our application and client CRMs.

Results
This architecture proved remarkably successful. Administrators could add, remove, or modify custom properties in minutes. After the initial client analysis, we could deploy a fully configured new instance within hours (or slightly longer when custom logic was needed).
Client-specific customizations were implemented in isolated modules without touching the core codebase. Updates and bug fixes were seamlessly deployed through our automated release system, keeping all instances current.
We also implemented comprehensive monitoring through the ELK stack, giving us centralized visibility into application logs across all deployments (here’s my article on the topic).
Thanks for reading! I hope this real-world example provides useful insights for your architectural challenges.
.