Skip to main content
The Application Layer orchestrates business workflows by coordinating domain entities and external services. It contains use cases that represent specific user actions and application-level services.

Layer Overview

Location: ~/workspace/source/Chapi/Application/ Responsibilities:
  • Implement use cases (user stories)
  • Orchestrate domain entities and services
  • Define application-specific interfaces
  • Coordinate transactions and workflows
  • Transform data between layers
Dependencies:
  • ✅ Depends on Domain Layer (entities, interfaces)
  • ❌ No dependencies on Infrastructure or Presentation

Architecture Pattern

Chapi uses the Use Case pattern to organize application logic:

Use Cases

Use cases represent single user actions or business operations.

Git Use Cases

Project Use Cases

AI Use Cases

Application Services

Application services provide cross-cutting functionality for use cases.

Application Interfaces

Interfaces defined at the application level for infrastructure to implement.
namespace Chapi.Application.Interfaces.Workspace;

public interface IWorkspaceService
{
    Task<Result<WorkspaceData>> LoadWorkspaceAsync(string projectPath);
    Task<Result> SaveWorkspaceAsync(WorkspaceData data);
    Task<Result<string>> GetRandomQuoteAsync();
    Result OpenFileInExplorer(string filePath);
}
Source: Application/Interfaces/Workspace/IWorkspaceService.cs

Use Case Categories

Git Operations

  • CommitChangesUseCase
  • PushChangesUseCase
  • LoadHistoryUseCase
  • SwitchBranchUseCase
  • StashChangesUseCase

Project Management

  • CreateProjectUseCase
  • LoadProjectsUseCase
  • SwitchProjectUseCase
  • DeployProjectReleaseUseCase

AI Features

  • SendChatMessageUseCase
  • GenerateCommitMessageUseCase
  • GenerateSqlQueryUseCase

Code Generation

  • GenerateModuleUseCase
  • AddApiControllerUseCase
  • AddDependencyInjectionUseCase

Dependency Injection

Use cases are registered as transient services:
services.AddTransient<CommitChangesUseCase>();
services.AddTransient<CreateProjectUseCase>();
services.AddTransient<GenerateCommitMessageUseCase>();

services.AddSingleton<GeminiChatService>();
services.AddSingleton<IAssistantCapabilityRegistry, AssistantCapabilityRegistry>();

Request/Response Pattern

Many use cases follow a Request/Response pattern:
// Request object
public record CreateProjectRequest(
    string ProjectName,
    string ParentDirectory,
    string TemplateUrl,
    string RemoteUrl = null
);

// Use case execution
var request = new CreateProjectRequest(
    "MyApp",
    "C:/Projects",
    "https://github.com/template/repo"
);

var result = await _createProjectUseCase.ExecuteAsync(
    request, 
    progress => Console.WriteLine(progress)
);
Benefits:
  • Type-safe parameters
  • Immutable requests (using records)
  • Easy to test
  • Self-documenting API

Testing Use Cases

[Test]
public async Task CommitChanges_ShouldFail_WhenNoFilesSelected()
{
    // Arrange
    var mockGitRepo = new Mock<IGitRepository>();
    var mockNotifications = new Mock<INotificationService>();
    var useCase = new CommitChangesUseCase(mockGitRepo.Object, mockNotifications.Object);

    var request = new CommitRequest
    {
        ProjectPath = "/valid/path",
        Message = "Test commit",
        Files = Enumerable.Empty<string>()
    };

    // Act
    var result = await useCase.ExecuteAsync(request);

    // Assert
    Assert.IsFalse(result.IsSuccess);
    Assert.AreEqual("No hay archivos seleccionados para hacer commit", result.Error);
    mockNotifications.Verify(n => n.ShowWarning(It.IsAny<string>()), Times.Once);
}

Best Practices

Each use case handles one user action or business operation.
Always validate inputs before calling domain services:
var validation = Validate(request);
if (!validation.IsSuccess)
    return Result.Fail(validation.Error);
For long-running operations, provide progress callbacks:
public async Task<Result<string>> ExecuteAsync(
    CreateProjectRequest request, 
    Action<string> onProgress = null)
{
    onProgress?.Invoke("Step 1...");
    // ...
    onProgress?.Invoke("Step 2...");
}
Return Result<T> instead of throwing exceptions for expected failures.

Domain Layer

Core entities and interfaces

Infrastructure Layer

Implementation of services

Presentation Layer

ViewModels consuming use cases

Build docs developers (and LLMs) love