Files
baya-monorepo/server
hamid 2f2aec61a2 backend phase 1: config, reference & platform signals
Lay the cross-cutting platform backbone every later phase reads from. Adds
the first marketplace EF migration baseline (new `ops` schema) and the
mechanisms b2..b15 reuse: typed runtime config, an append-only audit trail,
an analytics event log, the holiday/bank-closure calendar, in-app
notifications, and the internal support-alert worklist.

Schema & migration
- New `ops` schema + migration InitialMarketplaceBaseline with 6 tables:
  PlatformConfigs (IAuditable), AuditLogs (append-only), SystemEvents,
  IranianHolidays, Notifications, SupportAlerts — with indexes/uniques and
  FKs to usr.Users. Seeded 12 config keys + 7 sample holidays via HasData.

Domain / Application
- IAuditable marker + [AuditRedacted] attribute; entities + string-code
  constant holders (config data_type, holiday type, alert type/severity/status).
- Facade contracts: IPlatformConfig, IHolidayCalendar, IAnalyticsSink,
  IAuditLogger, INotificationService, ISupportAlertService; DTOs +
  PagedResult<T>; evolved the INotificationDispatcher.Notification record to
  carry Type + DataJson; Pagination helper.
- 14 CQRS commands/queries (+ validators) wiring the endpoints to the facades.

Infrastructure
- DB-backed facade implementations in Persistence/Services/; real in-app
  INotificationDispatcher (removes the b0 log stub); notification-retention
  hosted service (purge is_read=1 AND age>90d).
- Extended AuditFieldInterceptor to also append an old/new-diff audit_logs row
  for every IAuditable change in the same transaction (PII redacted).
- Registered all facades + hosted service in AddPersistenceServices; removed
  the dispatcher registration from AddCrossCuttingSeams.

API
- 5 controllers: admin PlatformConfig/Holidays/Audit/SupportAlerts
  ([Authorize(DynamicPermission)]) + current-user Notifications ([Authorize]),
  all tenant-scoped and paginated. 16 Swagger paths total.

Money-correctness & safety rules honoured
- Config read at compute time (cached, parsed by data_type), never hardcoded;
  every config change is audited in the same transaction; audit_logs is
  append-only (no update/delete path); support alerts are admin-only;
  notifications are tenant-scoped; analytics is fire-and-forget.

Tests & docs
- 18 new foundation tests over in-memory SQLite (config typing + audit,
  holidays, notifications + tenancy + retention, support alerts, analytics);
  build clean (0 new code warnings), 22 tests green; migration applied to the
  dev DB and swagger.v1.json refreshed.
- Updated server Project map + CONVENTIONS, product data-model doc 12 (seeded
  config defaults), config-reference contract, mock registry, backend handoff/
  STATUS/report.

Follow-ups: add FK constraints for SupportAlerts.BookingId (b9) and ReviewId
(b14) when those tables land.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 01:18:00 +03:30
..
2026-06-16 01:32:43 +03:30
2026-06-16 01:46:53 +03:30
2026-06-21 00:05:07 +03:30
2026-06-16 01:46:53 +03:30
2026-06-16 01:46:53 +03:30
2026-06-16 01:32:43 +03:30
2026-06-21 00:05:07 +03:30

Balinyaar — Server (ASP.NET Core, Clean Architecture)

Backend API for the Balinyaar application. It is an ASP.NET Core (.NET 10) solution organized around Clean Architecture, with:

  • CQRS via martinothamar/Mediator (source-generated; not MediatR)
  • ASP.NET Core Identity with JWE (signed + encrypted JWT) and OTP authentication
  • Dynamic, permission-based authorization
  • EF Core persistence (SQL Server) with the Repository + Unit of Work patterns
  • A modular gRPC plugin mounted via Application Parts
  • Observability out of the box: Serilog, OpenTelemetry, Prometheus metrics, health checks

Looking for an architecture/file map to navigate the code? See CLAUDE.md (agent guide) and CONVENTIONS.md (coding rules).

Requirements

  • .NET 10 SDK
  • SQL Server (local instance, or the containerized one from docker-compose.yml)

Running locally

From the server/ folder:

dotnet restore Baya.sln
dotnet build Baya.sln
dotnet run --project src/API/Baya.Web.Api/Baya.Web.Api.csproj

By default the API listens on https://localhost:5002 and serves Swagger UI at /swagger.

On startup the app applies EF Core migrations and seeds default users automatically, so a reachable database (see appsettings.jsonConnectionStrings) is required.

Configuration

Settings live in src/API/Baya.Web.Api/appsettings.json (+ appsettings.Development.json):

  • ConnectionStrings:SqlServer — main application database
  • ConnectionStrings:logDb — Serilog SQL sink database
  • IdentitySettingsSecretKey (signing), Encryptkey (AES-128 encryption, exactly 16 chars), Issuer, Audience, token lifetimes

Running with Docker

Generate a development HTTPS certificate (used by the container):

dotnet dev-certs https -ep $env:USERPROFILE/.aspnet/https/baya.pfx -p Strong@Password
dotnet dev-certs https --trust

Build and start the API together with SQL Server 2022:

docker build -t bobby-baya -f Dockerfile .
docker-compose up -d

The compose stack exposes the API on http://localhost:5000 / https://localhost:5001 and SQL Server on localhost:1435.

Solution layout

src/
├── Core/
│   ├── Baya.Domain          Entities + domain primitives (the core / "brain")
│   └── Baya.Application      CQRS features, contracts (interfaces), MediatR pipeline
├── Infrastructure/
│   ├── Baya.Infrastructure.Persistence    EF Core DbContext, configs, repositories, UoW, migrations
│   ├── Baya.Infrastructure.Identity        Identity, JWE/JWT, OTP, dynamic permissions
│   ├── Baya.Infrastructure.CrossCutting    Cross-cutting concerns (logging)
│   └── Baya.Infrastructure.Monitoring      Health checks, OpenTelemetry, Prometheus
├── API/
│   ├── Baya.Web.Api          Presentation: REST controllers, Program.cs (startup)
│   ├── Baya.WebFramework     Reusable web config: base controller, filters, middleware, Swagger
│   └── Plugins/Baya.Web.Plugins.Grpc   Self-contained gRPC plugin
├── Shared/
│   └── Baya.SharedKernel     Shared extensions/helpers referenced by every layer
└── Tests/                        xUnit test setup + Identity tests

The layers (why they exist)

Domain

The core of the project. Each entity may carry its own behavior. Entities derive from a common BaseEntity, which lets the persistence layer discover models via reflection to register them and drive migrations.

Application

Routes requests and defines the contracts (interfaces) the system depends on, without knowing their implementations. This is where CQRS lives: Commands and Queries are kept separate and dispatched to their handlers by Mediator (martinothamar/Mediator, source-generated). Cross-cutting concerns (validation, metrics) are applied as Mediator pipeline behaviors.

Infrastructure

Implements the contracts the Application layer declares — the parts needed to run in the real world:

  • Persistence — the chosen database (SQL Server via EF Core). Repositories give self-describing, persistence-agnostic data access; Unit of Work keeps multi-step writes atomic and consistent.
  • Identity — registration, authentication and authorization using ASP.NET Core Identity, with JWE tokens, OTP login, and a dynamic access-control system.
  • CrossCutting — services used across the whole app, such as logging.
  • Monitoring — health checks, distributed tracing/metrics, and Prometheus.

WebFramework

Keeps Program.cs thin by moving each piece of configuration into its own reusable class (filters, middleware, Swagger, API versioning, the base controller, etc.).

Web.Api

The presentation layer — an ASP.NET Core Web API exposing versioned REST controllers under Controllers/V1.

Web.Plugins.Grpc

A standalone module that adds gRPC endpoints to the same host via Application Parts, giving modularity (the "plugin" middle ground between a monolith and microservices) without a separate deployment. It registers its services and pipeline through extension methods called from Program.cs.

Tests

dotnet test Baya.sln

Each layer is designed to be testable in isolation; Baya.Tests.Setup provides the shared test scaffolding.

License

See LICENSE.md.