Skip to content

Golden Path

Standardized patterns and best practices for building applications on the Forge platform.


📋 Overview

The Golden Path represents the recommended way to build applications using Forge. Following these patterns ensures consistency, maintainability, and takes advantage of platform capabilities.


🎯 Golden Path Philosophy

The Golden Path represents:

  • Standardized approaches that work for most cases
  • Best practices vetted by the platform team
  • Reduced cognitive load through conventions
  • Flexibility when needed - you can deviate, but know why

🏗️ Core Patterns

API Layer (Experience APIs)

// ✅ Golden Path: Use minimal APIs with service pattern
app.MapGet("/users/{id}", async (int id, IUserService service) =>
    await service.GetAsync(id) is User user
        ? Results.Ok(user)
        : Results.NotFound());

// ✅ Golden Path: Keep controllers thin
[ApiController]
public class UsersController(IUserService service) : ControllerBase
{
    [HttpGet("{id}")]
    public async Task<ActionResult<User>> Get(int id) =>
        await service.GetAsync(id) is User user ? Ok(user) : NotFound();
}

// ❌ Anti-pattern: Business logic in controller
[HttpGet("{id}")]
public async Task<ActionResult<User>> Get(int id)
{
    // Don't do database calls, complex logic here
    var user = await _context.Users
        .Include(u => u.Roles)
        .Where(u => u.Id == id)
        .FirstOrDefaultAsync();
    // ... complex processing
}

Service Layer

// ✅ Golden Path: Interface-based services
public interface IUserService
{
    Task<User?> GetAsync(int id);
    Task<User> CreateAsync(CreateUserRequest request);
}

public class UserService(IUserRepository repository) : IUserService
{
    public async Task<User?> GetAsync(int id) =>
        await repository.FindByIdAsync(id);
}

// ✅ Register in DI
builder.Services.AddScoped<IUserService, UserService>();

Data Access

// ✅ Golden Path: Repository pattern for CosmosDB
public class UserRepository(CosmosClient client) : IUserRepository
{
    private Container Container => client.GetContainer("db", "users");

    public async Task<User?> FindByIdAsync(string id)
    {
        try
        {
            var response = await Container.ReadItemAsync<User>(id, new PartitionKey(id));
            return response.Resource;
        }
        catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
        {
            return null;
        }
    }
}

Authentication

// ✅ Golden Path: Use Forge auth extensions
builder.AddOpenIdConnectServices();

// In middleware
app.UseOpenIdConnectServices();

// ❌ Anti-pattern: Custom JWT validation
builder.Services.AddAuthentication()
    .AddJwtBearer(options => { /* Custom config */ });

Configuration

# ✅ Golden Path: Use settings.yml for app settings
# infra/api/settings.yml
- name: "FeatureFlags__NewDashboard"
  value: "true"
- name: "CosmosDb__DatabaseName"
  value: "mydb"
// ✅ Access via configuration
var dbName = configuration["CosmosDb:DatabaseName"];

// ❌ Anti-pattern: Hardcoded values
var dbName = "production-db";

Service Discovery

// ✅ Golden Path: Use .NET Service Discovery for service-to-service calls
builder.Services.AddServiceDiscovery();
builder.Services.ConfigureHttpClientDefaults(http =>
{
    http.AddServiceDiscovery();
});

// Reference services by name (resolved via Aspire)
builder.Services.AddHttpClient<IPaymentClient>(client =>
{
    client.BaseAddress = new Uri("https+http://payment-api");
});

// ❌ Anti-pattern: Hardcoded service URLs
builder.Services.AddHttpClient<IPaymentClient>(client =>
{
    client.BaseAddress = new Uri("https://payment-api.internal.com");
});

In Aspire AppHost, services are automatically discoverable:

var paymentApi = builder.AddProject<Projects.PaymentApi>("payment-api");

builder.AddProject<Projects.MyApi>("my-api")
    .WithReference(paymentApi);  // Enables service discovery

Infrastructure

# ✅ Golden Path: Use Forge modules
module "saif-appservices" {
  source  = "app.terraform.io/SAIFCorp/saif-apiservice/azure"
  version = ">= 3.0.0, < 4.0.0"

  # Standard variables
  owner       = local.variables.owner
  project_id  = local.variables.project_id
  environment = var.environment
}

# ❌ Anti-pattern: Raw Azure resources
resource "azurerm_app_service" "api" {
  # Custom configuration
}

🤔 Decision Framework

When to Follow the Golden Path

  • Starting new projects
  • Standard use cases (CRUD APIs, web apps)
  • Team is new to the technology
  • Long-term maintainability is priority

When to Deviate

  • Unique performance requirements
  • Legacy system integration
  • Specialized domain needs
  • After discussing with platform team

📊 Pattern Recommendations

By Use Case

Need Golden Path
REST API Minimal APIs or Controllers with Services
Database CosmosDB with repository pattern
Auth Forge OIDC extensions
Secrets Azure Key Vault via settings.yml
Messaging Azure Service Bus with Aspire
Frontend React with Vite

By File Type

File Pattern
Program.cs Builder → Configure → Run
*.csproj Central package management
infra/*.tf Forge modules + generated files
settings.yml Configuration over hardcoding

✅ Code Review Checklist

When reviewing for Golden Path adherence:

  • Uses Forge packages (not raw Azure SDKs)
  • Configuration via settings.yml
  • No hardcoded secrets or URLs
  • Service/repository pattern for logic
  • Minimal business logic in controllers
  • Uses Aspire for orchestration
  • Infrastructure uses Forge modules
  • Tests follow Forge patterns

💬 Guidance Examples

Gentle Guidance

"I see you're accessing the database directly from the controller. The Golden Path pattern would be to create a repository service. This makes testing easier and keeps controllers focused on HTTP concerns."

Explaining Trade-offs

"The Golden Path uses CosmosDB for most data storage. If you need relational data with complex joins, SQL might be better - but let's discuss with the platform team first to understand the trade-offs."