Audit trails are essential for modern enterprise applications. They help teams track what changed, who changed it, and when it changed. But raw audit data is often difficult for users to understand.
A well-designed side-by-side comparison UI solves this by visually showing old vs new values for each field.
This article explains how to design, architect, and implement a field-level audit comparison UI using Angular (frontend) and ASP.NET Core (backend), along with recommended data structure, API endpoints, and UI layout patterns.
As per your preferences, this article includes:
Flowchart
Workflow
ER Diagram
Architecture Diagram (Visio-style)
Sequence Diagram (small header style)
Beginner-friendly but professional Indian English
No emoji
1. Overview
The goal is to build an Audit Trail module where users can open a record’s history and immediately see:
| Field Name | Old Value | New Value | Changed By | Changed On |
|---|
The UI highlights differences so that business users can review updates quickly.
2. Why Side-by-Side Comparison?
Because it:
Makes change reviews faster
Helps QA teams validate data updates
Adds transparency for approval workflows
Reduces back-and-forth in audits
Works well for Master/Transactional data
3. High-Level Flowchart
+-----------------------+
| User opens record |
+-----------+-----------+
|
v
+-----------------------+
| Fetch audit history |
+-----------+-----------+
|
v
+-----------------------------+
| Transform data (old vs new) |
+-------------+---------------+
|
v
+----------------------------------------+
| Render side-by-side comparison in UI |
+------------------+---------------------+
|
v
+------------------------------+
| User filters, searches, etc. |
+------------------------------+
4. Workflow for Audit Processing
User selects a record
Angular requests audit logs using record ID
ASP.NET Core fetches audit entries from DB
Server groups audit entries by modification timestamp
Server prepares field-level JSON:
fieldName, oldValue, newValue, changedBy, changedOn
Angular displays both values side-by-side
UI highlights changed values
5. ER Diagram (Audit Trail)
Smaller Header
ER Diagram
+-------------------+ +-----------------------+
| User | | AuditHeader |
|-------------------| |-----------------------|
| UserId (PK) | 1 n | AuditId (PK) |
| Name |--------| EntityName |
| Email | | EntityId |
+-------------------+ | ChangedBy (FK) |
| ChangedOn |
+-----------+-----------+
|
| 1
|
n
+---------------------------+
| AuditDetail |
|---------------------------|
| AuditDetailId (PK) |
| AuditId (FK) |
| FieldName |
| OldValue |
| NewValue |
+---------------------------+
6. Architecture Diagram (Visio-Style)
Smaller Header
Architecture Diagram
+---------------------------+
| Angular UI |
| Audit Compare Screen |
+-------------+-------------+
|
v
+-----------------------+
| ASP.NET Core API |
| /audit/{entityId} |
+-----------+-----------+
|
v
+-----------------------+
| Audit Service |
| (mapping + grouping) |
+-----------+-----------+
|
v
+-------------+
| Database |
| Audit Tables|
+-------------+
7. Sequence Diagram (Smaller Header)
Sequence Diagram
User -> Angular: Open Audit History
Angular -> API: GET /audit/123
API -> DB: SELECT * FROM AuditHeader + AuditDetail
DB -> API: Return audit rows
API -> API: Transform to field-level changes
API -> Angular: Return JSON result
Angular -> Angular: Render comparison grid
Angular -> User: Display side-by-side values
8. Backend Structure (ASP.NET Core)
Recommended API endpoint
GET /api/audit/{entityName}/{entityId}
Sample Model
public class AuditChangeDto
{
public string FieldName { get; set; }
public string OldValue { get; set; }
public string NewValue { get; set; }
public string ChangedBy { get; set; }
public DateTime ChangedOn { get; set; }
}
Controller
[HttpGet("{entityName}/{entityId}")]
public async Task<IActionResult> GetAudit(string entityName, int entityId)
{
var result = await _auditService.GetChanges(entityName, entityId);
return Ok(result);
}
9. Angular UI Structure
Component Structure
audit-viewer/
├── audit-viewer.component.ts
├── audit-viewer.component.html
├── audit-viewer.component.css
└── audit.service.ts
HTML (Side-by-side layout)
<table class="table audit-table">
<thead>
<tr>
<th>Field</th>
<th>Old Value</th>
<th>New Value</th>
<th>Changed By</th>
<th>Changed On</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of auditData">
<td>{{ row.fieldName }}</td>
<td [class.changed]="row.oldValue !== row.newValue">{{ row.oldValue }}</td>
<td [class.changed]="row.oldValue !== row.newValue">{{ row.newValue }}</td>
<td>{{ row.changedBy }}</td>
<td>{{ row.changedOn | date:'short' }}</td>
</tr>
</tbody>
</table>
CSS for highlighting
.changed {
font-weight: bold;
background-color: #ffe6e6;
}
10. Best Practices for an Audit Trail UI
Keep the UI clean
Show only changed fields. Avoid noise.
Allow filtering
Filter by user, date and field name.
Provide grouped view
Group changes by modification timestamp.
Avoid storing large values
Use hashing or separate blob storage for files or large text.
Mask sensitive data
Mask personal data where necessary.
11. Final Thoughts
A side-by-side audit comparison UI significantly improves transparency and helps teams validate data modifications easily. Implementing this in ASP.NET Core + Angular is straightforward when the architecture, data structure and workflow are well planned.