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
.csfile usesnamespace X.Y.Z;(no braces) sealedon classes that are not designed for inheritance- No
#regionblocks — organize with separate files or partial classes when code grows large - Central package management via
Directory.Packages.props— never specify versions in.csprojfiles
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
_camelCasefor 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
sealedmodifier (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}Dtofor read models,{Entity}Responsefor API responses,{Entity}Requestfor 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:
- Extracts data from the
EventEnvelope.Datadictionary usingTryGetValue - Validates entity existence and field values
- 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:
Project files reference packages without versions:
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