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."