Files
baya-monorepo/server
hamid f77a23cb25 backend phase 5: service catalog & nurse pricing variants
Two-tier service model the marketplace is priced and searched on. Admin
catalog skeleton (categories + EAV option groups/values, addable as data
not migrations; NULL category = cross-category) and the nurse pricing layer
(nurse_service_variants — the atomic bookable unit: category + one value per
required dimension at the nurse's own IRR price and price unit).

- New `catalog` schema via one additive migration; Price BIGINT (no floats),
  on the wire as a string of digits; total = price + unit + session_count.
- Duplicate-listing guard: deterministic option_set_hash + filtered
  UNIQUE(nurse_id, service_category_id, option_set_hash) WHERE deleted_at IS
  NULL + friendly 409 pre-check. One value per dimension; required groups
  (incl. cross-category) enforced; deactivate, never delete.
- Public catalog browse cached behind a CatalogCache generation token,
  invalidated on any admin write. IVariantSnapshotSerializer shipped for b8.
- Contract (catalog.md) + handoff + report published; swagger refreshed.
  122 tests green; zero new build warnings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 22:21:53 +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.