Fafp.Templates.Console.Minimal
1.0.1
dotnet new install Fafp.Templates.Console.Minimal@1.0.1
Faf.Console.Minimal — Minimal Console Boilerplate
Console application template for testing hypotheses on 2–10 classes. One scenario — one folder.
Purpose
The template is designed for testing technical hypotheses in a console application. Target size — two to ten classes. One scenario is placed in one folder.
Scope Boundary
The template ends where Result<T>, CancellationToken from the host, appsettings, custom log formatters, HTTP, and other infrastructure begin.
File Structure
/
├── README.md # English versionRussian version
├── README.ru.md # Russian version
├── Program.cs # orchestration: host, try/catch/finally, exit code
├── AppRunner.cs # scenario entry point, resolved from DI
├── Features/
│ └── <Hypothesis>/ # one scenario — one folder
│ ├── <scenario classes>
│ └── ...
├── Properties/
│ └── launchSettings.json # DOTNET_ENVIRONMENT=Development
├── Directory.Build.props # nullable, warnings-as-errors, LangVersion
└── <project>.csproj
Placement rule. All classes related to the scenario are placed in Features/<Hypothesis>/. Only Program.cs, AppRunner.cs, and build files remain at the root. Removing a hypothesis reduces to removing one folder.
Inside a feature, classes are placed flat while their count does not exceed 7–8. Beyond that — grouping by purpose within the same folder.
Relationship Diagram
Program.cs (top-level)
│
├── creates ────► LoggerFactory (early logger)
│ │
│ └── used by Program.cs for catch logging
│
├── builds ─────► Host.CreateApplicationBuilder
│ │
│ ├── provides ──► ILogger (AddSimpleConsole)
│ │
│ └── provides ──► IServiceProvider
│ │
│ └── injects ──► AppRunner
│ │
│ └── invokes ──► Features/<Hypothesis>
│
├── creates ────► CancellationTokenSource ◄── cancels ── Console.CancelKeyPress
│ │
│ └── token ──► AppRunner ──► Features/<Hypothesis>
│
└── catches ────► Exception / OperationCanceledException ──► early logger
Execution flow. Program.cs creates the early logger, builds the host, resolves AppRunner from DI, runs the scenario in Features/, catches unhandled exceptions in two catch blocks, sets the exit code, and stops the host in finally.
Code Conventions
Bilingual Comments
Comments in code are written in Russian and English, separated by /.
// Prevent immediate process kill / Не даём процессу умереть сразу
e.Cancel = true;
Section Comments
Logical blocks in files are marked with double-divider sections:
// ═══════════════════════════════════════════════════════════════════════════════
// Section name / Название секции
// ═══════════════════════════════════════════════════════════════════════════════
Nested sections inside try use a single divider:
// ─── Build host / Сборка хоста ───
Bilingual Log Messages
Runtime log messages are also bilingual, separated by /:
logger.LogInformation("Готово / Done");
logger.LogError(ex, "Непредвиденная ошибка / Unhandled error");
Code Markers
A marker is left where hypothesis logic is expected to be written:
// ─── Здесь код гипотезы / Hypothesis code goes here ───
Error Handling
Business errors do not reach catch blocks: in minimal there is no business layer. Only infrastructure and unhandled exceptions reach Program.cs.
| Situation | Exit code | Level | Logged |
|---|---|---|---|
| Successful completion | 0 | Information | "Готово / Done" |
OperationCanceledException |
130 | Information | "Прервано пользователем" |
| Other exceptions | 1 | Error | ex.ToString() (stacktrace) |
Implementation requirements.
catch (OperationCanceledException)— first, without stacktrace. Otherwisecatch (Exception)would intercept it, sinceOperationCanceledExceptionderives fromSystemException.catch (Exception ex)— second,ex.ToString()in full. The only place in the template where a stacktrace is written.finally—StopAsyncandDispose. Both wrapped inif (host is not null), becausetrycovers initialization and the host may not have been built.StopAsync— inside an innertry/catch: an exception in thefinallyof the outertryis not caught by thecatchblocks and kills the process.return Environment.ExitCode;at the end ofMain— mandatory. Without it, top-level returns 0 and overwrites the set code.
Logging
Microsoft.Extensions.LoggingwithAddSimpleConsole.- Parameters:
TimestampFormat = "HH:mm:ss.fff ",SingleLine = true,IncludeScopes = true, level color — default. - Default level —
Information. - Serilog, JSON format, and custom formatter are not included.
Two Logger Factories
Program.cs uses two independent factories:
- Early (
LoggerFactory.Create) — created beforetry, used byProgram.csitself. Needed becausetrycovers host initialization, andcatchmust have somewhere to write. - Host — created inside
builder.Build(), used byAppRunnerand features viaILogger<T>.
This is a deliberate decision. Cost — three lines and one local ConfigureLogging function shared by both factories. Benefit — try covers everything, including host construction.
Both factories write to Console.Out. Console writes are line-thread-safe; interleaving is practically impossible.
Logging in finally
In finally, "Остановка хоста / Stopping host" is written before StopAsync and "Ошибка остановки хоста / Host stop failed" — on exception from StopAsync. The latter is inside an inner try/catch so that a stop exception does not overwrite Environment.ExitCode.
Cancellation
Cancellation source — the Console.CancelKeyPress event:
- subscribe to the event;
e.Cancel = truefor graceful host shutdown;- cancel the
CancellationTokenSource; - the token is passed to
AppRunnerand further to the scenario.
OperationCanceledException propagates through the stack and is caught by the first catch block. SIGTERM handling via PosixSignalRegistration is not included.
Directory.Build.props
Nullable = enableImplicitUsings = enableLangVersion = latestTreatWarningsAsErrors = trueInvariantGlobalization = true
These settings are introduced from the start: adding them later involves reworking existing code.
Important. The file is located inside faf-console-minimal/, next to the .csproj. If placed higher up the tree, it will not be copied into the generated project, and the sandbox will be left without settings.
Out of Scope
| Component | Rationale |
|---|---|
Result<T> |
No business layer in minimal |
ExpectedException |
Not needed when using Result<T> |
CancellationToken from host |
CancelKeyPress is sufficient |
appsettings.json |
Logging configuration is done in code |
appsettings.Production.json |
No production environment |
Custom ConsoleFormatter |
AddSimpleConsole covers the main scenarios |
TimeProvider |
Needed only when working with time in a scenario |
| Serilog, JSON logs | Redundant for terminal output |
HTTP, IHttpClientFactory |
Not part of minimal |
| MediatR, FluentValidation, AutoMapper | Belong to the application layer |
| Docker, CI, tests by default | Infrastructure is added as needed |
Creating a Project from the Template
Install
dotnet new install <path-to-template>/faf-console-minimal
Verify
dotnet new list | findstr faf
Generate
cd <sandbox-dir>
dotnet new faf-console-minimal -n MyHypothesis --dry-run
--dry-run is mandatory before the first generation. It shows the file list and substitutions without writing anything to disk. Allows catching forgotten Faf.Console.Minimal occurrences in the code.
If --dry-run shows correct substitutions:
dotnet new faf-console-minimal -n MyHypothesis
Creates a MyHypothesis/ folder with the project. The namespace, .csproj, and root namespace inside files become MyHypothesis.
Template Development
The template engine does not pick up changes on the fly. When editing template contents — full cycle:
dotnet new uninstall faf-console-minimal
:: ...edits in faf-console-minimal/...
dotnet new install <path-to-template>/faf-console-minimal
dotnet new faf-console-minimal -n Scratch --dry-run
Post-generation check. All occurrences of Faf.Console.Minimal must be replaced with -n: the .csproj name, namespace in each .cs, RootNamespace in the .csproj, and the profile name in launchSettings.json.
Principles
- The project is ready for writing business logic immediately after generation from the template.
- The host is used as a source of DI and
ILoggerwithout additional wiring. - Error handling: infrastructure errors — via a top-level
catch (Exception). - Stacktrace is logged exactly once — for unhandled exceptions.
- Additional components are introduced as needed, not in advance.
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.