Introduction
This article explains how to build a simple, production-minded Customer Support Ticket application using ASP.NET Core for the backend and Angular for the frontend. It focuses on practical implementation: source structure, models, APIs, authentication, Angular services and components, database design, and real-world best practices.
The guide is written in clear Indian English and is aimed at senior developers who want a copy-pasteable, well-structured reference.
High-Level Overview
The application supports:
User authentication (agents and customers) using JWT
Creating, updating, and closing support tickets
Ticket assignment and comments (threaded messages)
File attachments (handled via blob storage)
Role-based authorization (Agent, Admin, Customer)
Basic dashboard with filters (status, priority, assignee)
Components
ASP.NET Core Web API (controllers, services, EF Core)
Angular SPA (services, components, state management)
SQL Server for relational data
Optional: Azure Blob Storage or S3 for attachments
Architecture Diagram (ASCII)
[Angular SPA] <----HTTPS/JWT----> [ASP.NET Core Web API]
| |
| +---> [SQL Server (EF Core)]
| |
+---> [File Uploads] --------------> [Blob Storage (Azure/AWS)]
Database Design (ERD)
Keep schema small but practical. Main tables:
Users(Id, UserName, Email, PasswordHash, Role, CreatedAt)
Tickets(Id, Title, Description, CustomerId, AssigneeId, Priority, Status, CreatedAt, UpdatedAt)
TicketComments(Id, TicketId, AuthorId, Content, CreatedAt)
Attachments(Id, TicketId, FileName, ContentType, BlobUrl, CreatedAt)
Relationships
Users (1) --- (M) Tickets (as CustomerId)
Users (1) --- (M) Tickets (as AssigneeId)
Tickets (1) --- (M) TicketComments
Tickets (1) --- (M) Attachments
Use FK constraints, indexes on Status, Priority, AssigneeId, and CreatedAt for efficient querying.
Backend: Project Structure
Create an ASP.NET Core Web API project named SupportBackend.
Suggested folders
Controllers
Models (EF entities and DTOs)
Services (business logic)
Repositories (data access - optional)
Hubs (for real-time notifications if needed)
Migrations
Helpers (JWT, file helpers)
Backend: Models (EF Core)
Ticket.cs
public class Ticket
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public Guid CustomerId { get; set; }
public Guid? AssigneeId { get; set; }
public TicketPriority Priority { get; set; }
public TicketStatus Status { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public DateTimeOffset UpdatedAt { get; set; }
public List<TicketComment> Comments { get; set; } = new();
public List<Attachment> Attachments { get; set; } = new();
}
TicketComment.cs
public class TicketComment
{
public Guid Id { get; set; }
public Guid TicketId { get; set; }
public Guid AuthorId { get; set; }
public string Content { get; set; }
public DateTimeOffset CreatedAt { get; set; }
}
Attachment.cs
public class Attachment
{
public Guid Id { get; set; }
public Guid TicketId { get; set; }
public string FileName { get; set; }
public string ContentType { get; set; }
public string BlobUrl { get; set; }
public DateTimeOffset CreatedAt { get; set; }
}
Enums
public enum TicketPriority { Low, Medium, High, Critical }
public enum TicketStatus { Open, InProgress, Resolved, Closed }
Register these in SupportDbContext and add migrations.
Backend: DTOs and Mapping
Use DTOs to avoid over-posting and for versioning. Use AutoMapper or manual mapping.
TicketCreateDto
public class TicketCreateDto
{
public string Title { get; set; }
public string Description { get; set; }
public TicketPriority Priority { get; set; }
}
TicketDto (returned to client)
public class TicketDto
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string CustomerName { get; set; }
public string AssigneeName { get; set; }
public TicketPriority Priority { get; set; }
public TicketStatus Status { get; set; }
public DateTimeOffset CreatedAt { get; set; }
}
Backend: Services and Repositories
Keep business logic in services. Example ITicketService:
public interface ITicketService
{
Task<TicketDto> CreateTicketAsync(Guid customerId, TicketCreateDto dto);
Task<PagedResult<TicketDto>> GetTicketsAsync(TicketQuery query);
Task<TicketDto> GetTicketAsync(Guid ticketId);
Task AddCommentAsync(Guid ticketId, Guid authorId, string content);
Task AssignTicketAsync(Guid ticketId, Guid agentId);
Task UpdateStatusAsync(Guid ticketId, TicketStatus status);
}
Implement TicketService using EF Core with transactions where necessary.
Best practice: Validate permissions in service layer (e.g., only agents can assign tickets).
Backend: Controllers (API endpoints)
Create TicketsController with endpoints:
POST /api/tickets — create ticket (Customer)
GET /api/tickets — list tickets with filters and pagination
GET /api/tickets/{id} — get ticket details
POST /api/tickets/{id}/comments — add comment
POST /api/tickets/{id}/assign — assign to agent (Agent/Admin)
POST /api/tickets/{id}/status — update status
POST /api/tickets/{id}/attachments — upload files
Use [Authorize] on controllers and [Authorize(Roles = "Agent,Admin")] for assignment endpoints.
Example: Create Ticket
[HttpPost]
public async Task<IActionResult> Create([FromBody] TicketCreateDto dto)
{
var userId = User.GetUserId(); // extension to get claim
var ticket = await _ticketService.CreateTicketAsync(userId, dto);
return CreatedAtAction(nameof(Get), new { id = ticket.Id }, ticket);
}
Authentication and Authorization
Use ASP.NET Core Identity or a lightweight JWT solution. For many corporate apps, using Identity with EF Core is fine.
Steps
Register Identity and JWT in Program.cs.
Secure endpoints with [Authorize] and role policies.
On login, return a JWT with sub as user id and role claims.
Important: Do not store plain passwords. Use secure hashing (Identity does this for you). Use short-lived JWTs and refresh tokens.
File Attachments
Files should be uploaded via controller to blob storage. Store metadata (BlobUrl) in Attachments table.
Controller snippet
[HttpPost("{id}/attachments")]
public async Task<IActionResult> Upload(Guid id, IFormFile file)
{
if (file.Length == 0) return BadRequest();
var blobUrl = await _blobService.UploadFileAsync(file);
await _ticketService.AddAttachmentAsync(id, file.FileName, file.ContentType, blobUrl);
return Ok();
}
Security: Scan content type, limit file size, and sanitize file names.
Optional: Real-time Notifications (SignalR)
Add SignalR hub to broadcast ticket events (new ticket, assigned, status changed, comment added). Agents can subscribe to events.
Hub events: TicketCreated, TicketAssigned, CommentAdded, TicketStatusChanged.
Why: Improves agent responsiveness and dashboard UX.
Frontend: Angular Project Structure
Create Angular app support-portal and organize:
src/app/core (auth, http interceptor, models)
src/app/tickets (services, components, ticket list, ticket detail)
src/app/shared (components like file uploader, pagination)
src/app/dashboard (agent dashboard)
Install libraries
npm install @auth0/angular-jwt
npm install @microsoft/signalr (optional for real-time)
Frontend: Authentication (JWT)
Implement AuthService to login and store token in localStorage. Use AuthInterceptor to attach the Bearer token to requests.
auth.service.ts
login(creds) { return this.http.post('/api/auth/login', creds).pipe(tap(res => localStorage.setItem('token', res.token))); }
logout() { localStorage.removeItem('token'); }
getToken() { return localStorage.getItem('token'); }
auth.interceptor.ts
intercept(req, next) {
const token = this.auth.getToken();
if (token) req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
return next.handle(req);
}
Frontend: Ticket Service
ticket.service.ts handles API calls and maps responses to client models.
Key methods
createTicket(dto)
getTickets(filter)
getTicket(id)
addComment(ticketId, comment)
assignTicket(ticketId, agentId)
updateStatus(ticketId, status)
uploadAttachment(ticketId, file)
Use RxJS BehaviorSubject for current ticket and list updates so components can react in real time.
Frontend: Components
Create the following components:
ticket-list — server-side pagination, filters for status and priority.
ticket-detail — shows ticket, comments, attachments, and add-comment form.
ticket-create — form to create new tickets with validations.
agent-dashboard — shows unassigned tickets and quick actions.
Ticket-create form: Use reactive forms with validation rules for Title (required), Description (min length), Priority.
Ticket-detail: Keep comments in a scrollable panel and provide optimistic UI for posting comments (show locally then confirm server response).
File Upload Component
Use <input type="file"> and FormData to POST to /api/tickets/{id}/attachments.
upload function
const fd = new FormData();
fd.append('file', file);
this.http.post(`/api/tickets/${id}/attachments`, fd, { reportProgress: true, observe: 'events' })
.subscribe(event => { /* handle progress and response */ });
Real-world Best Practices
Validation: Validate both client and server.
Pagination & Filters: Always use server-side pagination for ticket lists.
Transactions: Use transactions for ticket state changes that affect multiple tables.
Concurrency: Implement optimistic concurrency (RowVersion) to avoid lost updates.
Logging & Telemetry: Log user actions (not message content) and use Application Insights or ELK.
Rate limiting: Apply rate limits to APIs that can be abused (comment posting, file uploads).
Data retention: Add archival policies for old tickets.
Testing: Unit test services and integration test controllers with in-memory DB.
Security Considerations
Use HTTPS always.
Restrict CORS to trusted domains.
Use role-based authorization for sensitive endpoints.
Sanitize user input to prevent XSS.
Limit file upload size and scan files where possible.
Deployment Checklist
Use environment-specific configuration for DB connection, blob storage, and JWT keys.
Run EF migrations during deployment.
Use CI/CD pipelines to build, test, and deploy.
For scaling: run multiple API instances, use a load balancer, and share a common blob store and DB.
If using SignalR for notifications, choose Azure SignalR service or Redis backplane.
Monitoring and Alerts
Monitor:
API errors and failed requests
Average response time and queue length
Storage usage for attachments
Ticket creation rate and SLA breaches
Set alerts for increased error rates, high latency, and running out of storage.
Sample Sequence: Create Ticket and Notify Agent
Customer -> Angular -> POST /api/tickets
API -> Create ticket (DB) -> SignalR Hub broadcast TicketCreated to agents
Agent Dashboard (subscribed) receives event and refreshes list
Agent -> Assign ticket -> POST /api/tickets/{id}/assign -> API updates ticket -> Hub sends TicketAssigned
Testing Strategy
Unit tests for services and business logic
Controller tests using WebApplicationFactory and in-memory DB
E2E tests for Angular with Protractor or Cypress
Load test critical endpoints and file uploads
Example: Implementing CreateTicketAsync (Service)
public async Task<TicketDto> CreateTicketAsync(Guid customerId, TicketCreateDto dto)
{
var ticket = new Ticket
{
Id = Guid.NewGuid(),
Title = dto.Title,
Description = dto.Description,
CustomerId = customerId,
Priority = dto.Priority,
Status = TicketStatus.Open,
CreatedAt = DateTimeOffset.UtcNow,
UpdatedAt = DateTimeOffset.UtcNow
};
_db.Tickets.Add(ticket);
await _db.SaveChangesAsync();
var result = _mapper.Map<TicketDto>(ticket);
// notify via SignalR
await _notificationService.TicketCreated(result);
return result;
}
Next Steps and Extensions
Implement SLA tracking and automated escalation rules
Add search with full-text indexing
Add multi-tenant support for SaaS scenarios
Provide audit logs and export features
Conclusion
This article provided a full, practical plan to build a Customer Support Ticket application with ASP.NET Core and Angular. It included database design, API surface, Angular structure, security, file handling, real-time notifications, and production best practices.
If you want, I can now:
generate a ready-to-run GitHub repository scaffold with backend and frontend,
add detailed EF Core migration scripts and seed data,
include full Angular component code with styles and unit tests,
provide PNG/SVG diagrams (flowchart, ERD, sequence) and upload them into the document.
Tell me which extension you would like next.