A Use Case is a class that encapsulates a single business operation or user story. It represents one thing the user wants to accomplish in the application.
namespace Chapi.Application.UseCases.Git;// Request DTOpublic class CommitRequest{ public string ProjectPath { get; set; } = string.Empty; public string Message { get; set; } = string.Empty; public IEnumerable<string> Files { get; set; } = Enumerable.Empty<string>();}// Use Case implementationpublic class CommitChangesUseCase{ private readonly IGitRepository _gitRepo; private readonly INotificationService _notifications; public CommitChangesUseCase( IGitRepository gitRepo, INotificationService notifications) { _gitRepo = gitRepo; _notifications = notifications; } public async Task<Result<GitCommit>> ExecuteAsync(CommitRequest request) { // 1. Validate input var validation = Validate(request); if (!validation.IsSuccess) { _notifications.ShowWarning(validation.Error); return Result<GitCommit>.Fail(validation.Error); } // 2. Execute business operation var result = await _gitRepo.CommitAsync( request.ProjectPath, request.Message, request.Files); // 3. Handle result and notify user if (result.IsSuccess) { _notifications.ShowSuccess($"Commit realizado: {request.Message}"); } else { _notifications.ShowError($"Error al hacer commit: {result.Error}"); } return result; } private Result Validate(CommitRequest request) { if (string.IsNullOrWhiteSpace(request.ProjectPath)) return Result.Fail("Ruta de proyecto invalida"); if (!Directory.Exists(request.ProjectPath)) return Result.Fail("El proyecto no existe"); if (string.IsNullOrWhiteSpace(request.Message)) return Result.Fail("Debes escribir un mensaje de commit"); if (!request.Files.Any()) return Result.Fail("No hay archivos seleccionados para hacer commit"); return Result.Success(); }}
Each Use Case should represent a single business operation. If a Use Case is doing multiple things, split it.
// ✅ Good: Focused responsibilitypublic class CommitChangesUseCase { }public class PushChangesUseCase { }// ❌ Bad: Too many responsibilitiespublic class GitOperationsUseCase { }
Use Request/Response DTOs
Define request and response objects for clarity:
public class CommitRequest { }public class CommitResponse { }public async Task<Result<CommitResponse>> ExecuteAsync(CommitRequest request)
Validate Inputs
Always validate inputs before executing business logic:
var validation = Validate(request);if (!validation.IsSuccess) return Result.Fail(validation.Error);
Return Results, Not Exceptions
Use the Result pattern for expected failures. Reserve exceptions for unexpected errors:
// ✅ Goodreturn Result<T>.Fail("Validation error");// ❌ Badthrow new ValidationException("Validation error");
Keep Use Cases Pure
Use Cases should not contain UI logic or framework-specific code:
private void ConfigureServices(IServiceCollection services){ // Use Cases are transient - new instance per request services.AddTransient<CommitChangesUseCase>(); services.AddTransient<CreateBranchUseCase>(); services.AddTransient<GenerateCommitMessageUseCase>(); // ...}
Use Cases are the heart of business logic in Clean Architecture:
Each represents one user operation
Orchestrates between domain and infrastructure
Highly testable through dependency injection
Reusable across different UI contexts
Independent of frameworks and UI
When adding a new feature, start by creating a Use Case. This forces you to think about the business operation independently of how it will be presented in the UI.