Skip to content

Rule reference

Thiago Botelho edited this page Aug 15, 2026 · 1 revision

Rule reference

Every rule in dotnet-modular-react-postgresql@1.0.0, what it checks, why it has the severity it has, and what to do when it fails.

All 21 rules expect their signal to be true. Six are blocking, fourteen are required, one is recommended. The maximum weighted score is 73 points: 6 × 5 + 14 × 3 + 1 × 1.

Reading a rule

{
  "id": "api.version-v1",
  "severity": "blocking",
  "description": "The public API uses the /api/v1 prefix.",
  "signal": "apiVersioning",
  "expected": true,
  "remediation": "Version public routes under /api/v1 and document the evolution policy."
}

The rule passes when the observed signal equals expected. The remediation string is what appears in the report and becomes the task text in the plan, so it is written as an instruction rather than as a complaint.

Severity, and why a rule blocks

blocking is reserved for two situations: a stack decision that would make the whole profile the wrong standard, or a security default whose absence is dangerous rather than merely untidy. Everything a team would reasonably fix next sprint is required. Everything that is good practice but context dependent is recommended.

One blocking failure makes the repository not compliant regardless of score. The full arithmetic is in Scoring and compliance.

Stack

stack.no-nestjs

blocking · signal forbiddenNestAbsent

The backend does not use NestJS.

Remove NestJS dependencies and keep the backend on ASP.NET Core.

Blocking because it is not a defect, it is a different architecture. A NestJS backend is not a partially compliant .NET system, so continuing to grade it against this profile produces a meaningless number. Either the repository belongs to a different Golden Path, or this dependency arrived by accident.

stack.no-nextjs

blocking · signal forbiddenNextAbsent

The frontend does not use Next.js.

Remove Next.js and keep the frontend as a React application.

Detected by a next dependency or by a next.config.* file. The profile treats server-side rendering as a decision that belongs to a different path, mostly because it changes where authorization and data fetching live, and this profile's security rules assume the API is the only trust boundary.

Architecture

architecture.modular-dotnet

blocking · signal modularDotnet

The .NET solution separates Domain, Application, and Infrastructure.

Organize the .NET solution into business modules with Domain, Application, and Infrastructure layers.

Blocking because it is the profile's central claim. This is the only rule whose signal is composite: it needs a .sln or .csproj and all three layer directories. A repository failing it will usually also fail the three rules below, which is redundancy on purpose. The three tell you which layer is missing, this one tells you the shape is wrong.

architecture.domain

required · signal domainLayer

There is an independent Domain layer.

Create the Domain layer and keep business rules and models there, free of infrastructure dependencies.

The signal only proves a Domain/ directory exists. It cannot prove independence. quality.architecture-tests is what actually enforces the dependency direction, and this rule is the cheap precondition for it.

architecture.application

required · signal applicationLayer

There is an Application layer for use cases.

Create the Application layer and make use cases, commands, queries, and ports explicit.

architecture.infrastructure

required · signal infrastructureLayer

There is an Infrastructure layer for external adapters.

Create the Infrastructure layer and keep persistence and external integrations there.

API

api.endpoints

required · signal endpointLayer

Controllers or endpoints form a thin HTTP layer.

Create thin Controllers or Endpoints that delegate execution to use cases.

Satisfied by a Controllers/ or Endpoints/ directory. Minimal APIs declared entirely inside Program.cs will fail this rule even though they are a perfectly good design. Group them into an Endpoints/ directory to pass, which is the layout the profile recommends anyway.

api.version-v1

blocking · signal apiVersioning

The public API uses the /api/v1 prefix.

Version public routes under /api/v1 and document the evolution policy.

Blocking because an unversioned public API is a decision you cannot undo cheaply. Once external clients depend on /orders, introducing versioning means breaking them or maintaining two shapes forever.

The detector wants a quoted literal containing /api/v1. Attribute routes written as [Route("api/v1/orders")] miss, because the pattern expects the leading slash inside the quotes. Route prefixes assembled by concatenation also miss. See the signal reference.

api.openapi

required · signal openApi

The API publishes an OpenAPI contract.

Enable OpenAPI and validate the generated document in the pipeline.

Matches AddOpenApi, MapOpenApi, AddSwaggerGen, or UseSwagger, so both the .NET 9+ built-in and Swashbuckle satisfy it.

api.problem-details

required · signal problemDetails

HTTP errors follow Problem Details.

Standardize errors with RFC 9457 Problem Details and stable domain codes.

The weakest signal in the profile: any mention of the ProblemDetails type passes it. Do not read a pass here as evidence that your error contract is consistent.

Security

security.jwt

required · signal jwtBearer

The API authenticates JWT Bearer tokens issued by the OIDC provider.

Configure JWT Bearer with validated issuer, audience, signature, and clock skew.

Required rather than blocking, because an internal service behind a gateway that terminates authentication elsewhere is a legitimate design. If that is your case, the honest resolution is a profile of your own rather than ignoring the finding.

security.policies

required · signal authorizationPolicies

Authorization is expressed through policies.

Define policies per business capability and apply them to the endpoints.

Matches AddPolicy or IAuthorizationHandler. Role checks scattered through [Authorize(Roles = "...")] attributes will not satisfy it, which is the point: the policy asks for authorization expressed as named capabilities rather than as role strings repeated across controllers.

security.default-deny

blocking · signal defaultDeny

Authorization uses default deny.

Set an authenticated FallbackPolicy and grant anonymous access only where it is declared.

Blocking because the failure mode is silent. An endpoint added without an [Authorize] attribute is public, nobody notices in review, and the first person to notice is whoever finds it. A FallbackPolicy inverts that: forgetting the attribute produces a 401 rather than a leak.

The signal only checks that the word FallbackPolicy appears somewhere in a .cs file. It does not verify the value assigned to it.

Database

database.postgresql-efcore

required · signal postgresEfCore

Persistence uses PostgreSQL through the EF Core Npgsql provider.

Configure EF Core with Npgsql and keep data access inside Infrastructure.

Matches the package name or a UseNpgsql call, in any text file, so a PackageReference in a .csproj is enough.

database.migrations

required · signal migrations

Database changes are versioned as migrations.

Add versioned migrations and validate them before deployment.

The path pattern is case-sensitive and expects Migrations/. A lowercase migrations/ directory fails. This is the most common false negative in the profile.

Frontend

frontend.react

blocking · signal reactWeb

The web interface uses React without Next.js.

Create a React application and organize features by domain.

Requires a react dependency and the absence of next. A repository with no frontend at all fails this rule, which is the correct answer for a profile whose name promises React, and the wrong answer for an API-only service. An API-only service should use a profile that does not claim a frontend.

Quality

quality.unit-tests

required · signal unitTests

Domain rules and use cases have unit tests.

Add unit tests for domain rules and for critical use cases.

Detected by a UnitTests/ directory. It measures the convention, not coverage.

quality.integration-tests

required · signal integrationTests

HTTP and PostgreSQL integrations have automated tests.

Add integration tests for the API and for PostgreSQL persistence.

Detected by an IntegrationTests/ directory.

quality.architecture-tests

recommended · signal architectureTests

Dependencies between layers are protected by architecture tests.

Add architecture tests that prevent unwanted dependencies between layers.

The only recommended rule, and the only one worth arguing about. It is what actually keeps architecture.domain honest over time, since a directory named Domain/ stops meaning anything the day someone adds a DbContext reference to it. It stays recommended because a small team can reasonably hold that line by review, and a rule nobody can satisfy on day one gets ignored.

Detected by an ArchitectureTests/ or ArchTests/ directory.

Operations

operations.health-checks

required · signal healthChecks

The application exposes operational health checks.

Configure liveness and readiness, including the PostgreSQL dependency.

Matches AddHealthChecks or MapHealthChecks. It cannot tell liveness from readiness, and it cannot tell whether the PostgreSQL dependency is included. The remediation asks for more than the signal can verify, on purpose: it is written for the person doing the work, not for the detector.

operations.structured-logging

required · signal structuredLogging

Logs are structured and preserve correlation.

Configure ILogger or Serilog with structured properties and a correlation ID.

Matches Serilog or ILogger< in any text file. Nearly every ASP.NET Core application passes this by default, so treat it as a floor rather than as evidence that correlation actually works.

Summary table

Rule Severity Signal
stack.no-nestjs blocking forbiddenNestAbsent
stack.no-nextjs blocking forbiddenNextAbsent
architecture.modular-dotnet blocking modularDotnet
architecture.domain required domainLayer
architecture.application required applicationLayer
architecture.infrastructure required infrastructureLayer
api.endpoints required endpointLayer
api.version-v1 blocking apiVersioning
api.openapi required openApi
api.problem-details required problemDetails
security.jwt required jwtBearer
security.policies required authorizationPolicies
security.default-deny blocking defaultDeny
database.postgresql-efcore required postgresEfCore
database.migrations required migrations
frontend.react blocking reactWeb
quality.unit-tests required unitTests
quality.integration-tests required integrationTests
quality.architecture-tests recommended architectureTests
operations.health-checks required healthChecks
operations.structured-logging required structuredLogging

Clone this wiki locally