---
title: "The Application layer is where our use cases live."
url: https://daily.dev/posts/the-application-layer-is-where-our-use-cases-live--omd6hgerz
source_url: https://daily.dev/posts/the-application-layer-is-where-our-use-cases-live--omd6hgerz
type: freeform
source: "Abdul Rafique"
author: "Abdul Rafique"
published: 2026-05-10T02:19:51.851Z
updated: 2026-05-10T02:20:15.915Z
tags: ["architecture", ".net", "c#"]
reading_time: 6
upvotes: 0
comments: 0
language: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The Application layer is where our use cases live.

**[Abdul Rafique](https://daily.dev/sources/53faz7ubl)** · [@abdul_rafique](https://daily.dev/abdul_rafique) · 6 min read · 0 upvotes · 0 comments

## Summary

A practical walkthrough of building the Application layer in a .NET Clean Architecture project called SGAcademy. Covers service interfaces and implementations using the IRepositoryWrapper pattern, DTO design (three DTOs per entity), AutoMapper profiles, folder structure, typed error handling, and the principle that services should orchestrate rather than contain business logic. Includes concrete C# code examples for a student enrollment service.

## Content

> The domain defines what your business is. The Application layer defines what your system can do. Get the boundary wrong and everything bleeds together.
> 

---

## Day 4 of building SGAcademy in public

Yesterday I built the domain layer — entities, value objects, enums, and the base `AuditableEntity` that stamps every record with created/updated metadata.

Today: the Application layer.

In SGAcademy this layer lives in `SGAcademy.Application`. Its job is to expose use cases — the actual operations your system supports — through services that the API layer calls.

No business logic leaking into controllers. No database code here. Just clean orchestration.

---

## What lives in the Application layer

Three things:

- **Service interfaces** — contracts that define what each feature area can do
- **Service implementations** — the code that orchestrates domain objects and repositories
- **DTOs + AutoMapper profiles** — the shapes of data going in and out

That's it. The Application layer depends on the Domain layer. It defines interfaces for repositories. It never touches Entity Framework directly — that's Infrastructure's job.

---

## The pattern: Service → IRepositoryWrapper → Repository

Every service in SGAcademy follows the same structure. Here's the student enrollment service as a real example:

**Service interface** → `Contracts/Services/IStudentService.cs`

```csharp
public interface IStudentService
{
    Task<IEnumerable<StudentDto>> GetAllAsync();
    Task<StudentDto> GetByIdAsync(Guid id);
    Task<StudentDto> CreateAsync(CreateStudentDto dto);
    Task<StudentDto> UpdateAsync(Guid id, UpdateStudentDto dto);
    Task DeleteAsync(Guid id);
}
```

**Service implementation** → `Services/StudentService.cs`

```csharp
public class StudentService : IStudentService
{
    private readonly IRepositoryWrapper _repository;
    private readonly IMapper _mapper;

    public StudentService(IRepositoryWrapper repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }

    public async Task<StudentDto> GetByIdAsync(Guid id)
    {
        var student = await _repository.Student.GetByIdAsync(id)
            ?? throw new RecordNotFoundError($"Student {id} not found.");

        return _mapper.Map<StudentDto>(student);
    }

    public async Task<StudentDto> CreateAsync(CreateStudentDto dto)
    {
        var student = _mapper.Map<Student>(dto);
        _repository.Student.Create(student);
        await _repository.SaveAsync();
        return _mapper.Map<StudentDto>(student);
    }

    public async Task DeleteAsync(Guid id)
    {
        var student = await _repository.Student.GetByIdAsync(id)
            ?? throw new RecordNotFoundError($"Student {id} not found.");

        _repository.Student.Delete(student);
        await _repository.SaveAsync();
    }

    // ... UpdateAsync, GetAllAsync
}
```

The service never touches `DbContext`. It goes through `IRepositoryWrapper`, which aggregates every repository behind a single injectable dependency. `SaveAsync()` is called once after all mutations — that's the unit of work.

---

## DTOs: the shapes of data

The Application layer owns all DTOs. Three per entity, as a rule:

```csharp
// StudentDto.cs — what the client receives
public class StudentDto
{
    public Guid Id { get; set; }
    public string FullName { get; set; } = string.Empty;
    public string RegistrationNumber { get; set; } = string.Empty;
    public DateOnly DateOfBirth { get; set; }
    public string Status { get; set; } = string.Empty;
}

// CreateStudentDto.cs — what the client sends on POST
public class CreateStudentDto
{
    public string FullName { get; set; } = string.Empty;
    public string RegistrationNumber { get; set; } = string.Empty;
    public DateOnly DateOfBirth { get; set; }
}

// UpdateStudentDto.cs — what the client sends on PUT
public class UpdateStudentDto
{
    public string FullName { get; set; } = string.Empty;
    public DateOnly DateOfBirth { get; set; }
}
```

The entity never leaves the Application layer. The client always gets a DTO. AutoMapper handles the translation.

---

## AutoMapper profiles

Every entity gets its own mapping profile:

```csharp
public class StudentMappingProfile : BaseMappingProfile
{
    public StudentMappingProfile()
    {
        CreateMap<Student, StudentDto>();
        CreateMap<CreateStudentDto, Student>();
        CreateMap<UpdateStudentDto, Student>();
    }
}
```

`BaseMappingProfile` is a shared base class in SGAcademy that all profiles extend. AutoMapper is registered once in `Application/Extensions/ServiceExtensions.cs` and picks up all profiles automatically.

---

## Folder structure for SGAcademy.Application

Here's the exact structure I set up today:

```csharp
SGAcademy.Application/
├── Contracts/
│   └── Services/
│       ├── IStudentService.cs
│       ├── IStaffService.cs
│       ├── IClassService.cs
│       ├── IAttendanceService.cs
│       └── IEnrollmentService.cs
├── Services/
│   ├── StudentService.cs
│   ├── StaffService.cs
│   ├── ClassService.cs
│   ├── AttendanceService.cs
│   └── EnrollmentService.cs
├── DTOs/
│   ├── Student/
│   │   ├── StudentDto.cs
│   │   ├── CreateStudentDto.cs
│   │   └── UpdateStudentDto.cs
│   ├── Staff/
│   ├── Class/
│   ├── Attendance/
│   └── Enrollment/
├── Mappings/
│   ├── StudentMappingProfile.cs
│   ├── StaffMappingProfile.cs
│   └── ...
└── Extensions/
    └── ServiceExtensions.cs
```

One folder per domain area. DTOs grouped by entity. Interfaces and implementations kept separate.

---

## Error handling

Services throw typed errors from `SGAcademy.Shared`. The API layer catches and maps them to HTTP responses.

```csharp
// 404 — entity not found
throw new RecordNotFoundError($"Student {id} not found.");

// 409 — conflict (e.g. duplicate registration number)
throw new ConflictError("A student with this registration number already exists.");

// 400 — validation failure
throw new ValidationError("Date of birth cannot be in the future.");
```

No `try/catch` in services. No HTTP status codes. That's the API layer's concern. The service just describes what went wrong.

---

## What I actually built today

For Day 4 of SGAcademy, I shipped the Application layer for five domain areas:

- `StudentService` — full CRUD + enrollment status management
- `StaffService` — full CRUD with department and designation lookups
- `ClassService` — create, update, get by academic year
- `AttendanceService` — mark attendance, get by class and date range
- `EnrollmentService` — enroll student in class, get class roster

All service interfaces registered in `ServiceExtensions.cs`. All AutoMapper profiles set up. All DTOs defined and mapped.

Zero Entity Framework references in this layer. Zero HTTP concerns. Pure orchestration.

---

## The mistake that kills this pattern

Making your services too smart.

I've seen codebases where services have 500-line methods, nested conditionals, raw SQL strings, and HTTP calls. The service becomes the god object — everything ends up there because "it's easier."

The rule: services orchestrate. Domain objects enforce business rules. Repositories handle data access. Keep each layer doing exactly one thing.

When you feel like your service is getting complicated, it's usually a sign that logic belongs in the domain entity — not in the service.

---

## One principle to take away

The Application layer is the public API of your backend's logic. It's what your controllers talk to. It's what you'd test first if something broke.

Keep it clean. Keep it thin. Name your methods after what the user is trying to accomplish, not after database operations.

`EnrollStudentInClass()` — not `UpdateStudentRecord()`.

The name should tell the story.

---

## Tomorrow

Day 5: Infrastructure layer — implementing the repositories, wiring up Entity Framework Core configurations, and setting up SQL Server. Where the clean theory meets actual database tables.

---

*Learn | Build | Ship — BuildWithRafique `<R/>`*

#buildinpublic #dotnet #cleanarchitecture #devtips #learntocode #webdev #sgacademy

---

Tags: [#architecture](https://daily.dev/tags/architecture), [#.net](https://daily.dev/tags/.net), [#c#](https://daily.dev/tags/c#)

[View this post on daily.dev](https://daily.dev/posts/the-application-layer-is-where-our-use-cases-live--omd6hgerz)
