Skip to content

Design patterns

The API follows a layered architecture that separates HTTP concerns, business logic, domain contracts, and infrastructure details.

Why it is used

  • Keeps controllers thin.
  • Makes core logic easier to test and reason about.
  • Prevents persistence and vendor details from leaking everywhere.

Repository interfaces live in Domain/Interfaces and EF-backed implementations live in Infrastructure/Repositories/Ef.

Why it is used

  • Application services depend on abstractions rather than EF types.
  • Storage concerns stay replaceable and localized.
  • User-scoping and blob-path logic stay centralized.

ShotAnalysisPipeline acts as an orchestrator for multiple smaller steps: loading swing averages, analyzing the lie, loading personalization, and requesting stance guidance.

Why it is used

  • The AI workflow is naturally multi-stage.
  • Fallback behavior can be inserted cleanly.
  • Future agent expansion can happen without rewriting controller code.

4. Transport-client mirroring across platforms

Section titled “4. Transport-client mirroring across platforms”

The web, iOS, and Android apps each implement a thin shared-client concept:

  • Axios client in the web app
  • URLSession wrapper in iOS
  • OkHttp wrapper in Android

Why it is used

  • Each platform gets native ergonomics.
  • The mental model stays consistent across teams.
  • Contract bugs are easier to compare and debug.

When AI agent calls fail, the platform still returns a recommendation using built-in logic. This avoids blocking the golfer’s round on an integration outage.

Azure endpoints, auth settings, and storage settings are configured through environment-aware configuration instead of being hard-coded into business logic.

[HttpGet]
public async Task<IActionResult> GetAll([FromQuery] string? user)
{
var userId = ResolveUserId(user);
var photos = userId is null
? await _photoService.GetAllPhotosAsync()
: await _photoService.GetPhotosForUserAsync(userId);
return Ok(photos.Select(MapToResponse));
}

This is the preferred pattern for new API endpoints: keep the controller responsible for HTTP concerns and keep use-case logic in services.