Code Quality Design Help

Type-State Builder Pattern

The type-state builder pattern uses interface progression to enforce compile-time safety, ensuring objects are constructed with all required properties in the correct order. Invalid states become impossible to represent.

Interface Progression

using System.Net; using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Mvc; namespace Shared.Types; public interface IProblemBuilder<T> { IProblemBuilderWithSource<T> WithSourceId( string sourceId); } public interface IProblemBuilderWithSource<T> { IProblemBuilderWithTitle<T> WithTitle(string title); } public interface IProblemBuilderWithTitle<T> { IProblemBuilderWithDetail<T> WithDetail( string detail); } public interface IProblemBuilderWithDetail<T> { IProblemBuilderComplete<T> WithStatusCode( HttpStatusCode statusCode); } public interface IProblemBuilderComplete<T> { ProblemDetailsResult<T> Create( [CallerMemberName] string callerMemberName = ""); }

Implementation

internal class ProblemBuilder<T> : IProblemBuilder<T>, IProblemBuilderWithSource<T>, IProblemBuilderWithTitle<T>, IProblemBuilderWithDetail<T>, IProblemBuilderComplete<T> { private string? _sourceId; private string? _title; private string? _detail; private HttpStatusCode _statusCode; public IProblemBuilderWithSource<T> WithSourceId( string sourceId) { _sourceId = sourceId; return this; } public IProblemBuilderWithTitle<T> WithTitle( string title) { _title = title; return this; } public IProblemBuilderWithDetail<T> WithDetail( string detail) { _detail = detail; return this; } public IProblemBuilderComplete<T> WithStatusCode( HttpStatusCode statusCode) { _statusCode = statusCode; return this; } public ProblemDetailsResult<T> Create( [CallerMemberName] string callerMemberName = "") { return new ProblemDetailsResult<T> { SourceId = _sourceId!, StatusCode = _statusCode, ProblemDetails = new ProblemDetails { Title = _title!, Detail = _detail!, Status = (int)_statusCode }, CallerMemberName = callerMemberName }; } }

SOLID Principles Demonstrated

  • Single Responsibility Principle: Each interface represents one step in the construction process

  • Open/Closed Principle: Extensible without modifying existing interfaces

  • Interface Segregation Principle: Clients depend only on interfaces they need at each step

  • Dependency Inversion Principle: Factory methods depend on abstractions, not concrete builder

See Also

10 November 2025