From 1ec261966ccd2101173edfefe6dc1e402ac30c85 Mon Sep 17 00:00:00 2001 From: Codex-Symphony Date: Fri, 31 Jul 2026 16:40:19 +0300 Subject: [PATCH 1/3] Bootstrap layered ASP.NET solution skeleton (Closes #1) --- .gitignore | 3 +++ .../Controllers/StatusController.cs | 23 ++++++++++++++++++ .../DemoApplication.Api.csproj | 14 +++++++++++ DemoApplication.Api/DemoApplication.Api.http | 6 +++++ DemoApplication.Api/Program.cs | 20 ++++++++++++++++ .../Properties/launchSettings.json | 23 ++++++++++++++++++ .../appsettings.Development.json | 9 +++++++ .../appsettings.Production.json | 9 +++++++ DemoApplication.Api/appsettings.json | 9 +++++++ DemoApplication.Api/wwwroot/index.html | 2 ++ .../Abstractions/IStartupCheck.cs | 8 +++++++ .../DemoApplication.Application.csproj | 13 ++++++++++ .../DemoApplication.Domain.csproj | 9 +++++++ DemoApplication.Domain/Placeholder.cs | 6 +++++ .../DemoApplication.Infrastructure.csproj | 14 +++++++++++ .../StartupCheck.cs | 13 ++++++++++ DemoApplication.slnx | 6 +++++ README.md | 24 ++++++++++++++++++- 18 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 DemoApplication.Api/Controllers/StatusController.cs create mode 100644 DemoApplication.Api/DemoApplication.Api.csproj create mode 100644 DemoApplication.Api/DemoApplication.Api.http create mode 100644 DemoApplication.Api/Program.cs create mode 100644 DemoApplication.Api/Properties/launchSettings.json create mode 100644 DemoApplication.Api/appsettings.Development.json create mode 100644 DemoApplication.Api/appsettings.Production.json create mode 100644 DemoApplication.Api/appsettings.json create mode 100644 DemoApplication.Api/wwwroot/index.html create mode 100644 DemoApplication.Application/Abstractions/IStartupCheck.cs create mode 100644 DemoApplication.Application/DemoApplication.Application.csproj create mode 100644 DemoApplication.Domain/DemoApplication.Domain.csproj create mode 100644 DemoApplication.Domain/Placeholder.cs create mode 100644 DemoApplication.Infrastructure/DemoApplication.Infrastructure.csproj create mode 100644 DemoApplication.Infrastructure/StartupCheck.cs create mode 100644 DemoApplication.slnx diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1608518 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +**/bin/ +**/obj/ +.vs/ diff --git a/DemoApplication.Api/Controllers/StatusController.cs b/DemoApplication.Api/Controllers/StatusController.cs new file mode 100644 index 0000000..998bd4b --- /dev/null +++ b/DemoApplication.Api/Controllers/StatusController.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Mvc; + +namespace DemoApplication.Api.Controllers; + +/// Exposes the API status contract used by smoke checks. +[ApiController] +[Route("api/status")] +public sealed class StatusController : ControllerBase +{ + /// Returns a stable response envelope for a running API. + [HttpGet] + public ActionResult> GetStatus() + { + return Ok(ApiResponse.Success(new { status = "ok" })); + } +} + +/// Represents a consistent API response envelope. +public sealed record ApiResponse(bool Succeeded, T? Data, IReadOnlyCollection Errors) +{ + /// Creates a successful response. + public static ApiResponse Success(T data) => new(true, data, Array.Empty()); +} diff --git a/DemoApplication.Api/DemoApplication.Api.csproj b/DemoApplication.Api/DemoApplication.Api.csproj new file mode 100644 index 0000000..3d663a6 --- /dev/null +++ b/DemoApplication.Api/DemoApplication.Api.csproj @@ -0,0 +1,14 @@ + + + + net10.0 + enable + enable + + + + + + + + diff --git a/DemoApplication.Api/DemoApplication.Api.http b/DemoApplication.Api/DemoApplication.Api.http new file mode 100644 index 0000000..27060ec --- /dev/null +++ b/DemoApplication.Api/DemoApplication.Api.http @@ -0,0 +1,6 @@ +@DemoApplication.Api_HostAddress = http://localhost:5271 + +GET {{DemoApplication.Api_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/DemoApplication.Api/Program.cs b/DemoApplication.Api/Program.cs new file mode 100644 index 0000000..70ffbff --- /dev/null +++ b/DemoApplication.Api/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Diagnostics; + +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); +builder.Services.AddProblemDetails(); +builder.Services.AddHealthChecks(); +builder.Services.AddControllers(); + +WebApplication app = builder.Build(); +app.UseExceptionHandler(); +app.UseDefaultFiles(); +app.UseStaticFiles(); +app.MapHealthChecks("/health"); +app.MapControllers(); +app.MapFallbackToFile("index.html"); + +app.Run(); + +public partial class Program +{ +} diff --git a/DemoApplication.Api/Properties/launchSettings.json b/DemoApplication.Api/Properties/launchSettings.json new file mode 100644 index 0000000..d3e6584 --- /dev/null +++ b/DemoApplication.Api/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5271", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7167;http://localhost:5271", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/DemoApplication.Api/appsettings.Development.json b/DemoApplication.Api/appsettings.Development.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/DemoApplication.Api/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/DemoApplication.Api/appsettings.Production.json b/DemoApplication.Api/appsettings.Production.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/DemoApplication.Api/appsettings.Production.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/DemoApplication.Api/appsettings.json b/DemoApplication.Api/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/DemoApplication.Api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/DemoApplication.Api/wwwroot/index.html b/DemoApplication.Api/wwwroot/index.html new file mode 100644 index 0000000..e726677 --- /dev/null +++ b/DemoApplication.Api/wwwroot/index.html @@ -0,0 +1,2 @@ + +Demo Application

Demo Application API

diff --git a/DemoApplication.Application/Abstractions/IStartupCheck.cs b/DemoApplication.Application/Abstractions/IStartupCheck.cs new file mode 100644 index 0000000..c72205f --- /dev/null +++ b/DemoApplication.Application/Abstractions/IStartupCheck.cs @@ -0,0 +1,8 @@ +namespace DemoApplication.Application.Abstractions; + +/// Defines an application startup validation check. +public interface IStartupCheck +{ + /// Validates required application dependencies. + Task CheckAsync(CancellationToken cancellationToken); +} diff --git a/DemoApplication.Application/DemoApplication.Application.csproj b/DemoApplication.Application/DemoApplication.Application.csproj new file mode 100644 index 0000000..1e3bf36 --- /dev/null +++ b/DemoApplication.Application/DemoApplication.Application.csproj @@ -0,0 +1,13 @@ + + + + + + + + net10.0 + enable + enable + + + diff --git a/DemoApplication.Domain/DemoApplication.Domain.csproj b/DemoApplication.Domain/DemoApplication.Domain.csproj new file mode 100644 index 0000000..b760144 --- /dev/null +++ b/DemoApplication.Domain/DemoApplication.Domain.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/DemoApplication.Domain/Placeholder.cs b/DemoApplication.Domain/Placeholder.cs new file mode 100644 index 0000000..4403a06 --- /dev/null +++ b/DemoApplication.Domain/Placeholder.cs @@ -0,0 +1,6 @@ +namespace DemoApplication.Domain; + +/// Marks the domain boundary for future business entities. +public sealed class Placeholder +{ +} diff --git a/DemoApplication.Infrastructure/DemoApplication.Infrastructure.csproj b/DemoApplication.Infrastructure/DemoApplication.Infrastructure.csproj new file mode 100644 index 0000000..7aa6826 --- /dev/null +++ b/DemoApplication.Infrastructure/DemoApplication.Infrastructure.csproj @@ -0,0 +1,14 @@ + + + + + + + + + net10.0 + enable + enable + + + diff --git a/DemoApplication.Infrastructure/StartupCheck.cs b/DemoApplication.Infrastructure/StartupCheck.cs new file mode 100644 index 0000000..afd7866 --- /dev/null +++ b/DemoApplication.Infrastructure/StartupCheck.cs @@ -0,0 +1,13 @@ +using DemoApplication.Application.Abstractions; + +namespace DemoApplication.Infrastructure; + +/// Provides the baseline startup check for infrastructure wiring. +public sealed class StartupCheck : IStartupCheck +{ + /// Completes successfully when baseline infrastructure is available. + public Task CheckAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} diff --git a/DemoApplication.slnx b/DemoApplication.slnx new file mode 100644 index 0000000..8fd79bb --- /dev/null +++ b/DemoApplication.slnx @@ -0,0 +1,6 @@ + + + + + + diff --git a/README.md b/README.md index 38d3d37..3937b81 100644 --- a/README.md +++ b/README.md @@ -1 +1,23 @@ -# demo-application \ No newline at end of file +# Demo Application + +API-first ASP.NET Core solution targeting .NET 10. + +## Architecture + +- `DemoApplication.Api` owns HTTP endpoints, middleware, health checks, and static SPA hosting. +- `DemoApplication.Application` owns use-case contracts and application orchestration. +- `DemoApplication.Domain` owns business entities and rules; it has no framework dependencies. +- `DemoApplication.Infrastructure` owns external integrations and implements Application abstractions. + +Dependencies point inward: API references Application and Infrastructure; Infrastructure references Application and Domain; Application references Domain. + +## Run + +From the repository root on Windows, Linux, or macOS: + +```bash +dotnet restore DemoApplication.slnx +dotnet run --project DemoApplication.Api +``` + +The API listens on the URL printed by the host. Verify startup with `GET /health` and `GET /api/status`. From 994cf341abef7381476ba1d569c2dd4e51363381 Mon Sep 17 00:00:00 2001 From: Codex-Symphony Date: Fri, 31 Jul 2026 16:45:52 +0300 Subject: [PATCH 2/3] [DEV-20260731-001-API] Complete startup validation wiring Refs: #1 --- DemoApplication.Api/Program.cs | 5 ++ .../StartupValidationHostedService.cs | 51 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 DemoApplication.Api/StartupValidationHostedService.cs diff --git a/DemoApplication.Api/Program.cs b/DemoApplication.Api/Program.cs index 70ffbff..7790311 100644 --- a/DemoApplication.Api/Program.cs +++ b/DemoApplication.Api/Program.cs @@ -1,9 +1,14 @@ +using DemoApplication.Application.Abstractions; +using DemoApplication.Api; +using DemoApplication.Infrastructure; using Microsoft.AspNetCore.Diagnostics; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); builder.Services.AddProblemDetails(); builder.Services.AddHealthChecks(); builder.Services.AddControllers(); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(); WebApplication app = builder.Build(); app.UseExceptionHandler(); diff --git a/DemoApplication.Api/StartupValidationHostedService.cs b/DemoApplication.Api/StartupValidationHostedService.cs new file mode 100644 index 0000000..0a53038 --- /dev/null +++ b/DemoApplication.Api/StartupValidationHostedService.cs @@ -0,0 +1,51 @@ +using DemoApplication.Application.Abstractions; + +namespace DemoApplication.Api +{ + +/// +/// Runs registered startup checks during host startup so unavailable infrastructure prevents serving requests. +/// +public sealed class StartupValidationHostedService : IHostedService +{ + private readonly IReadOnlyCollection _startupChecks; + + /// + /// Initializes the hosted validator with the checks owned by the application composition root. + /// + /// Registered checks that validate required startup dependencies. + public StartupValidationHostedService(IEnumerable startupChecks) + { + ArgumentNullException.ThrowIfNull( + argument: startupChecks, + paramName: nameof(startupChecks)); + + _startupChecks = startupChecks.ToArray(); + } + + /// + /// Executes every startup check before the host begins accepting requests. + /// + /// Token that cancels startup validation. + /// A task that completes after all checks pass. + public async Task StartAsync(CancellationToken cancellationToken) + { + // Run each dependency check before the server starts so a failure prevents partial availability. + foreach (var startupCheck in _startupChecks) + { + // Await the check with the host cancellation token so shutdown interrupts pending validation. + await startupCheck.CheckAsync(cancellationToken).ConfigureAwait(false); + } + } + + /// + /// Completes hosted-service shutdown without retaining startup resources. + /// + /// Token supplied by the host during shutdown. + /// A completed task because this validator owns no shutdown work. + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} +} From d716a1ac13e915964e68bb203c5a7c550e7158d8 Mon Sep 17 00:00:00 2001 From: Codex-Symphony Date: Fri, 31 Jul 2026 16:56:53 +0300 Subject: [PATCH 3/3] Refactor repository layout --- .github/workflows/release-pipeline.yml | 3 +++ README.md | 4 ++-- docs/learning-paths.md | 1 + .../DemoApplication.Api}/Controllers/StatusController.cs | 0 .../DemoApplication.Api}/DemoApplication.Api.csproj | 0 .../DemoApplication.Api}/DemoApplication.Api.http | 0 {DemoApplication.Api => src/DemoApplication.Api}/Program.cs | 0 .../DemoApplication.Api}/Properties/launchSettings.json | 0 .../DemoApplication.Api}/StartupValidationHostedService.cs | 0 .../DemoApplication.Api}/appsettings.Development.json | 0 .../DemoApplication.Api}/appsettings.Production.json | 0 .../DemoApplication.Api}/appsettings.json | 0 .../DemoApplication.Api}/wwwroot/index.html | 0 .../Abstractions/IStartupCheck.cs | 0 .../DemoApplication.Application.csproj | 0 .../DemoApplication.Domain}/DemoApplication.Domain.csproj | 0 .../DemoApplication.Domain}/Placeholder.cs | 0 .../DemoApplication.Infrastructure.csproj | 0 .../DemoApplication.Infrastructure}/StartupCheck.cs | 0 DemoApplication.slnx => src/DemoApplication.slnx | 0 20 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release-pipeline.yml create mode 100644 docs/learning-paths.md rename {DemoApplication.Api => src/DemoApplication.Api}/Controllers/StatusController.cs (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/DemoApplication.Api.csproj (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/DemoApplication.Api.http (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/Program.cs (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/Properties/launchSettings.json (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/StartupValidationHostedService.cs (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/appsettings.Development.json (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/appsettings.Production.json (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/appsettings.json (100%) rename {DemoApplication.Api => src/DemoApplication.Api}/wwwroot/index.html (100%) rename {DemoApplication.Application => src/DemoApplication.Application}/Abstractions/IStartupCheck.cs (100%) rename {DemoApplication.Application => src/DemoApplication.Application}/DemoApplication.Application.csproj (100%) rename {DemoApplication.Domain => src/DemoApplication.Domain}/DemoApplication.Domain.csproj (100%) rename {DemoApplication.Domain => src/DemoApplication.Domain}/Placeholder.cs (100%) rename {DemoApplication.Infrastructure => src/DemoApplication.Infrastructure}/DemoApplication.Infrastructure.csproj (100%) rename {DemoApplication.Infrastructure => src/DemoApplication.Infrastructure}/StartupCheck.cs (100%) rename DemoApplication.slnx => src/DemoApplication.slnx (100%) diff --git a/.github/workflows/release-pipeline.yml b/.github/workflows/release-pipeline.yml new file mode 100644 index 0000000..6560f3b --- /dev/null +++ b/.github/workflows/release-pipeline.yml @@ -0,0 +1,3 @@ +# Placeholder for the release pipeline. +# Triggers and jobs will be added when release automation is defined. +name: Release Pipeline diff --git a/README.md b/README.md index 3937b81..6aa41d6 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ Dependencies point inward: API references Application and Infrastructure; Infras From the repository root on Windows, Linux, or macOS: ```bash -dotnet restore DemoApplication.slnx -dotnet run --project DemoApplication.Api +dotnet restore src/DemoApplication.slnx +dotnet run --project src/DemoApplication.Api ``` The API listens on the URL printed by the host. Verify startup with `GET /health` and `GET /api/status`. diff --git a/docs/learning-paths.md b/docs/learning-paths.md new file mode 100644 index 0000000..516c959 --- /dev/null +++ b/docs/learning-paths.md @@ -0,0 +1 @@ +# Learning Paths diff --git a/DemoApplication.Api/Controllers/StatusController.cs b/src/DemoApplication.Api/Controllers/StatusController.cs similarity index 100% rename from DemoApplication.Api/Controllers/StatusController.cs rename to src/DemoApplication.Api/Controllers/StatusController.cs diff --git a/DemoApplication.Api/DemoApplication.Api.csproj b/src/DemoApplication.Api/DemoApplication.Api.csproj similarity index 100% rename from DemoApplication.Api/DemoApplication.Api.csproj rename to src/DemoApplication.Api/DemoApplication.Api.csproj diff --git a/DemoApplication.Api/DemoApplication.Api.http b/src/DemoApplication.Api/DemoApplication.Api.http similarity index 100% rename from DemoApplication.Api/DemoApplication.Api.http rename to src/DemoApplication.Api/DemoApplication.Api.http diff --git a/DemoApplication.Api/Program.cs b/src/DemoApplication.Api/Program.cs similarity index 100% rename from DemoApplication.Api/Program.cs rename to src/DemoApplication.Api/Program.cs diff --git a/DemoApplication.Api/Properties/launchSettings.json b/src/DemoApplication.Api/Properties/launchSettings.json similarity index 100% rename from DemoApplication.Api/Properties/launchSettings.json rename to src/DemoApplication.Api/Properties/launchSettings.json diff --git a/DemoApplication.Api/StartupValidationHostedService.cs b/src/DemoApplication.Api/StartupValidationHostedService.cs similarity index 100% rename from DemoApplication.Api/StartupValidationHostedService.cs rename to src/DemoApplication.Api/StartupValidationHostedService.cs diff --git a/DemoApplication.Api/appsettings.Development.json b/src/DemoApplication.Api/appsettings.Development.json similarity index 100% rename from DemoApplication.Api/appsettings.Development.json rename to src/DemoApplication.Api/appsettings.Development.json diff --git a/DemoApplication.Api/appsettings.Production.json b/src/DemoApplication.Api/appsettings.Production.json similarity index 100% rename from DemoApplication.Api/appsettings.Production.json rename to src/DemoApplication.Api/appsettings.Production.json diff --git a/DemoApplication.Api/appsettings.json b/src/DemoApplication.Api/appsettings.json similarity index 100% rename from DemoApplication.Api/appsettings.json rename to src/DemoApplication.Api/appsettings.json diff --git a/DemoApplication.Api/wwwroot/index.html b/src/DemoApplication.Api/wwwroot/index.html similarity index 100% rename from DemoApplication.Api/wwwroot/index.html rename to src/DemoApplication.Api/wwwroot/index.html diff --git a/DemoApplication.Application/Abstractions/IStartupCheck.cs b/src/DemoApplication.Application/Abstractions/IStartupCheck.cs similarity index 100% rename from DemoApplication.Application/Abstractions/IStartupCheck.cs rename to src/DemoApplication.Application/Abstractions/IStartupCheck.cs diff --git a/DemoApplication.Application/DemoApplication.Application.csproj b/src/DemoApplication.Application/DemoApplication.Application.csproj similarity index 100% rename from DemoApplication.Application/DemoApplication.Application.csproj rename to src/DemoApplication.Application/DemoApplication.Application.csproj diff --git a/DemoApplication.Domain/DemoApplication.Domain.csproj b/src/DemoApplication.Domain/DemoApplication.Domain.csproj similarity index 100% rename from DemoApplication.Domain/DemoApplication.Domain.csproj rename to src/DemoApplication.Domain/DemoApplication.Domain.csproj diff --git a/DemoApplication.Domain/Placeholder.cs b/src/DemoApplication.Domain/Placeholder.cs similarity index 100% rename from DemoApplication.Domain/Placeholder.cs rename to src/DemoApplication.Domain/Placeholder.cs diff --git a/DemoApplication.Infrastructure/DemoApplication.Infrastructure.csproj b/src/DemoApplication.Infrastructure/DemoApplication.Infrastructure.csproj similarity index 100% rename from DemoApplication.Infrastructure/DemoApplication.Infrastructure.csproj rename to src/DemoApplication.Infrastructure/DemoApplication.Infrastructure.csproj diff --git a/DemoApplication.Infrastructure/StartupCheck.cs b/src/DemoApplication.Infrastructure/StartupCheck.cs similarity index 100% rename from DemoApplication.Infrastructure/StartupCheck.cs rename to src/DemoApplication.Infrastructure/StartupCheck.cs diff --git a/DemoApplication.slnx b/src/DemoApplication.slnx similarity index 100% rename from DemoApplication.slnx rename to src/DemoApplication.slnx