Skip to content

Coding Conventions

This page documents the coding standards and patterns used throughout the Tech Strategy Tool codebase. Follow these conventions when contributing changes.

General Rules

  • Warnings-as-errors is enforced globally via Directory.Build.props — the build fails on any warning
  • File-scoped namespaces — every .cs file uses namespace X.Y.Z; (no braces)
  • sealed on classes that are not designed for inheritance
  • No #region blocks — organize with separate files or partial classes when code grows large
  • Central package management via Directory.Packages.props — never specify versions in .csproj files

Naming Conventions

Files

Pattern Location Purpose
*Entity.cs Infrastructure/Persistence/ EF Core entity models
*Endpoint.cs / *Endpoints.cs Api/Endpoints/ Minimal API route modules
*Dto.cs Shared/ Read model DTOs
*Response.cs Shared/ API response types
*Request.cs Shared/ API request types
*Store.cs / *Repository.cs Infrastructure/Persistence/ Persistence implementations

Classes and members

  • PascalCase for types, methods, properties, and constants
  • camelCase for local variables and parameters
  • _camelCase for private fields (if any)
  • Descriptive names that reflect purpose, not implementation

Project Layering

Dependency rules

Core          → (nothing - zero dependencies)
Shared        → (nothing)
Infrastructure → Core
UI            → Shared
Web           → UI, Shared
Api           → Infrastructure, Shared, Web

These are strict rules:

  • Core has zero NuGet dependencies and zero project references — pure domain logic only
  • Shared contains only DTOs — no business logic, no infrastructure
  • Infrastructure depends only on Core (plus EF Core NuGet packages)
  • UI depends only on Shared (plus Blazor component NuGet packages)
  • Api is the composition root that wires everything together

Do not add dependencies to Core

The TechStrat.Core project must remain dependency-free. This ensures the event processor can be tested without any infrastructure and guarantees the domain logic is portable.

Model Classes

Domain model classes in Core/Model/ are plain C# classes:

  • Mutable { get; set; } properties
  • List<T> collections (ordered, supports insert-at-index)
  • No sealed modifier (these are mutable document nodes)
  • No behavior — all logic lives in EventProcessor

Interface Records

Records in Core/Interfaces/ (e.g., StoredEvent, StoredCheckpoint, UserRecord) are immutable:

  • { get; init; } properties
  • Value-equality semantics
  • Used for cross-layer data transfer

DTOs

All DTOs live in TechStrat.Shared/:

  • Records — immutable, value-equality semantics
  • Naming: {Entity}Dto for read models, {Entity}Response for API responses, {Entity}Request for inputs
  • Collections use IReadOnlyList<T>
  • Positional parameters or { get; init; } syntax as appropriate

API Patterns

Minimal API endpoints

Endpoints are organized as static classes with MapXxx extension methods on IEndpointRouteBuilder:

public static class TeamEndpoints
{
    public static IEndpointRouteBuilder MapTeamEndpoints(this IEndpointRouteBuilder app)
    {
        app.MapGet("/api/teams", GetTeams).RequireAuthorization("ViewerOrAbove");
        return app;
    }

    private static async Task<IResult> GetTeams(...)
    {
        // ...
    }
}

Route patterns

  • /api/{resource} — top-level resources
  • /api/{parent}/{parentId}/{child} — nested resources

JSON serialization

System.Text.Json with camelCase property naming (ASP.NET Core default).

Event Processing Patterns

Handler methods

Each event handler:

  1. Extracts data from the EventEnvelope.Data dictionary using TryGetValue
  2. Validates entity existence and field values
  3. Returns a DispatchResult (never throws exceptions)

Defensive parsing

Always use TryGetValue/TryParse for data dictionary access. The processor must never throw — malformed data results in rejection, not exception.

Package Management

All NuGet package versions are centralized in Directory.Packages.props at the solution root:

<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />

Project files reference packages without versions:

<PackageReference Include="Microsoft.EntityFrameworkCore" />

Never specify versions in .csproj

Package versions must only be specified in Directory.Packages.props. Adding a version to a .csproj file causes a build error with central package management enabled.

Build Properties

Directory.Build.props at the solution root defines shared properties:

  • Target framework: net10.0
  • Nullable reference types: enabled
  • Implicit usings: enabled
  • Warnings-as-errors: enabled

These apply to all projects in the solution automatically.

Primary Constructors

Use primary constructors where they improve readability, particularly for dependency injection:

public sealed class EventStore(TechStratDbContext context) : IEventStore
{
    // context is available as a field
}

Further Reading

  • Testing — Test patterns and conventions
  • Migrations — Database migration workflow