backend phase 0: foundation, cross-cutting seams & starter cleanup

Remove the Order demo (entity/feature/repo/config/gRPC/proto) and the three
pre-marketplace migrations; regenerate a fresh InitialBaseline migration.

Stand up the REST surface (PingController + System/Ping CQRS) proving the
Mediator -> behaviors -> OperationResult -> ApiResult envelope end to end.

Close wiring gaps: register LoggingBehavior (outermost) and add the built-in
rate limiter (per-IP global + otp/auth/sensitive policies), placed before
authentication.

Add current-user + audit plumbing: ICurrentUser (HttpContext + null impls),
rename BaseEntity audit fields to CreatedAt/ModifiedAt (DateTimeOffset) +
CreatedById/ModifiedById, stamped by a new AuditFieldInterceptor.

Introduce five cross-cutting seams (IDateTimeProvider, IFieldEncryptor,
ICacheService, IObjectStorage, INotificationDispatcher) with in-memory/local
mocks registered via AddCrossCuttingSeams.

Add Baya.Test.Foundation (encryptor, audit interceptor, ping handler) and
update docs, contracts (swagger.v1.json), handoff, report, and mocks registry.
This commit is contained in:
hamid
2026-06-30 22:48:41 +03:30
parent 53a40dc51d
commit 765cc632d5
75 changed files with 1539 additions and 1418 deletions
@@ -0,0 +1,29 @@
using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Baya.Application.Features.System.Queries.Ping;
using Baya.WebFramework.Attributes;
using Baya.WebFramework.BaseController;
using Mediator;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Baya.WebFramework.ServiceConfiguration;
namespace Baya.Web.Api.Controllers.V1;
[ApiVersion("1")]
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[Display(Description = "Service liveness and pipeline-smoke endpoints")]
public sealed class PingController(ISender sender) : BaseController
{
[HttpGet("[action]")]
[ProducesOkApiResponseType<PingQueryResult>]
public async Task<IActionResult> GetStatus(CancellationToken cancellationToken)
=> OperationResult(await sender.Send(new PingQuery(), cancellationToken));
[HttpGet("[action]")]
[EnableRateLimiting(RateLimitingServiceExtension.GlobalPolicy)]
[ProducesOkApiResponseType<PingQueryResult>]
public async Task<IActionResult> GetStatusRateLimited(CancellationToken cancellationToken)
=> OperationResult(await sender.Send(new PingQuery(), cancellationToken));
}
+6 -1
View File
@@ -5,6 +5,7 @@ using Baya.Application.Models.Identity;
using Baya.Application.ServiceConfiguration;
using Baya.Domain.Entities.User;
using Baya.Infrastructure.CrossCutting.Logging;
using Baya.Infrastructure.CrossCutting.ServiceConfiguration;
using Baya.Infrastructure.Identity.Identity.Dtos;
using Baya.Infrastructure.Identity.Jwt;
using Baya.Infrastructure.Identity.ServiceConfiguration;
@@ -69,7 +70,9 @@ builder.Services.AddSwagger("v1","v1.1");
builder.Services.AddApplicationServices()
.RegisterIdentityServices(identitySettings)
.AddPersistenceServices(configuration)
.AddWebFrameworkServices();
.AddCrossCuttingSeams(configuration)
.AddWebFrameworkServices()
.AddRateLimitingPolicies();
builder.Services.RegisterValidatorsAsServices();
builder.Services.AddExceptionHandler<ExceptionHandler>();
@@ -105,6 +108,8 @@ app.UseSwaggerAndUi();
app.UseRouting();
app.UseRateLimiter();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
+10 -1
View File
@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"SqlServer": "Server=sql_server2022;Database=Baya_DB_Docker;User Id=SA;Password=A&VeryComplex123Password;MultipleActiveResultSets=true;encrypt=false",
"SqlServer": "Server=192.168.100.14,1433;Database=Baya;User Id=sa;Password=Pa##w0rd;TrustServerCertificate=True;Encrypt=False;",
"logDb": "Server=sql_server2022;Database=Baya_Log_DB_Docker;User Id=SA;Password=A&VeryComplex123Password;MultipleActiveResultSets=true;encrypt=false"
},
"IdentitySettings": {
@@ -11,6 +11,15 @@
"NotBeforeMinutes": "0",
"ExpirationMinutes": "10000"
},
"Seams": {
"FieldEncryption": {
"Key": "local-dev-field-encryption-key-change-me",
"HashKey": "local-dev-field-hash-key-change-me"
},
"ObjectStorage": {
"RootPath": ""
}
},
"AllowedHosts": "*",
"Kestrel": {
"EndpointDefaults": {