When building clean and easy-to-maintain APIs in .NET, a great approach is the Request–Endpoint–Response (REPR) pattern. It helps you organize your code clearly, keep parts loosely connected, and make your API easier to read and test.
What Is the REPR Pattern?
The REPR pattern gives a clear structure to your API endpoints by splitting them into three parts:
Request: The input model that defines what data the API needs.
Endpoint: The main part that processes the request, calls the business logic (using services, commands, or CQRS handlers), and prepares the response.
Response: The output model that defines what data is sent back to the client.
This pattern makes your API easier to understand, document, test, and update by clearly defining both the input and output.
Why Use REPR?
In traditional controllers, request handling, validation, and business logic often get mixed together, making the code messy and hard to manage. The REPR pattern solves this by keeping each part separate, which gives you:
Single Responsibility: Each part has one clear job.
Clear Contracts: Defined request and response models (DTOs).
Easier Testing: You can test the logic without depending on the web layer.
Consistency: All endpoints follow the same structure.
Clean Architecture Alignment: Fits well with clean and modular design principles.
Implementing the REPR Pattern in .NET
Let us see how it works in practice.
Step 1: Define the Request
namespace REPR.User
{
public sealed class CreateUserRequest
{
public string Email { get; init; }
public string Password { get; init; }
}
}
Step 2: Define the Response
namespace REPR.User
{
public sealed class CreateUserResponse
{
public Guid UserId { get; init; }
public string Message { get; init; }
}
}
Step 3: Define Endpoint
namespace REPR.User
{
[Route("api/[controller]")]
[ApiController]
public class CreateUserEndpoint : ControllerBase
{
[HttpPost]
[Route("create")]
public async Task<IActionResult> CreateUser([FromBody] CreateUserRequest request)
{
if (request == null
|| string.IsNullOrWhiteSpace(request.Email)
|| string.IsNullOrWhiteSpace(request.Password))
{
return BadRequest("Invalid user data.");
}
// Simulate user creation logic
var newUserId = Guid.NewGuid();
var response = new CreateUserResponse
{
UserId = newUserId,
Message = "User created successfully."
};
return Ok(response);
}
}
}
This structure clearly defines the role of each part:
Request → Defines the input
Endpoint → Acts as the orchestrator
Response → Defines the output
![REPR_01]()
Benefits in Large Systems
As your application gets bigger, the REPR pattern helps keep each endpoint organized and easy to manage. It fits well with Clean Architecture and Vertical Slice Architecture, where each feature (like “User Creation”) has its own request, handler, and response — kept separate from the rest.
This gives you:
No overloaded controllers
Easier testing and deployment for each feature
Simple setup for validation, mapping, and logging
REPR vs Traditional Controller–Service Pattern
Here’s an easy way to see why the REPR pattern is usually cleaner and easier to maintain than the traditional Controller–Service style:
| Aspect | Traditional Controller–Service Pattern | Request–Endpoint–Response (REPR) Pattern |
|---|
| Structure | Controllers handle many actions and rely on shared services. | Each endpoint has its own Request, Endpoint, and Response → one endpoint = one feature. |
| Responsibilities | Controllers often mix validation, business logic, and response formatting. | The endpoint only coordinates — validation, execution, and mapping are clearly separated. |
| Scalability | Controllers get large and harder to manage as the app grows. | Each endpoint is independent, easier to test, and scales well. |
| Testability | Requires mocking many services and controller layers. | Easy to test each endpoint with clear input and output models. |
| Discoverability | Business logic is scattered across controllers and services. | Feature-based structure makes it simple to find related code. |
| Fit with CQRS / Clean Architecture | Works, but may need refactoring. | Designed to work smoothly with CQRS and mediator patterns. |
Summary
The Request–Endpoint–Response (REPR) pattern is a simple and effective way to build clean, modular, and scalable APIs in .NET. It brings structure, consistency, and clear separation of responsibilities — perfect for both small and large applications.
Key points
Think of each endpoint as its own small feature.
Clearly define Request and Response models.
Keep business logic out of controllers — move it to handlers or services.
Use it with CQRS for even better separation between commands and queries.
Happy Coding!