Devlog: 2026-03-17 — .NET Solution Scaffold
What Happened
Scaffolded the .NET 8 modular monolith solution structure at src/backend/, implementing ADR-004 (modular monolith). The API container is now running in the dev profile and responding through the Nginx proxy.
Research Conducted
Before building, reviewed:
- .NET 8 modular monolith best practices (Clean Architecture, vertical slices, project organization)
- .NET Aspire evaluation (decided against -- overlaps with existing Docker Compose setup, can adopt later)
- EF Core 8 database-per-tenant patterns (scoped DbContext, connection string from JWT, no connection pooling)
- Docker multi-stage builds for .NET (using SDK image for dev, runtime image for production later)
- PowerSeller X project structure for ecosystem consistency patterns
Key Decisions
- Minimal APIs over Controllers -- lighter weight, modern convention, sufficient for ~20 endpoints
- Flat project structure -- one project per bounded context, not nested Clean Architecture layers
- No .NET Aspire -- we already have working Docker Compose + Nginx proxy infrastructure
- Framework Reference for ASP.NET Core in module projects -- allows modules to define their own Minimal API endpoints
- PowerSeller.SaaS namespace -- aligns with ecosystem (
PowerSeller.X,PowerSeller.MBSAccess)
Solution Structure
src/backend/
PowerSeller.SaaS.sln
Directory.Build.props
Dockerfile.dev
PowerSeller.SaaS.Api/ → Host (DI, middleware, Swagger, health)
PowerSeller.SaaS.SharedKernel/ → Abstractions (ITenantContext, IModule, Result)
PowerSeller.SaaS.Infrastructure/ → EF Core, multi-tenancy (TenantDbContext, TenantRegistry)
PowerSeller.SaaS.Modules.BestEx/ → BestEx module shell (status endpoint, empty Domain/Services/Endpoints)
tests/
PowerSeller.SaaS.Api.Tests/
PowerSeller.SaaS.Modules.BestEx.Tests/
What Was Built
| File | Purpose |
|---|---|
PowerSeller.SaaS.sln | Solution with 4 src + 2 test projects |
Directory.Build.props | Shared settings: net8.0, nullable enable, warnings as errors |
PowerSeller.SaaS.Api/Program.cs | Minimal API host with Swagger, CORS, health check, tenant middleware |
PowerSeller.SaaS.Api/Middleware/TenantMiddleware.cs | Extracts tenant from JWT/header, resolves connection string |
PowerSeller.SaaS.Api/Extensions/ServiceCollectionExtensions.cs | Multi-tenancy DI registration |
PowerSeller.SaaS.SharedKernel/ITenantContext.cs | Tenant context interface |
PowerSeller.SaaS.SharedKernel/IModule.cs | Module registration interface |
PowerSeller.SaaS.SharedKernel/Result.cs | Result pattern for domain operations |
PowerSeller.SaaS.Infrastructure/Data/TenantDbContext.cs | EF Core context with per-tenant connection string |
PowerSeller.SaaS.Infrastructure/Data/TenantRegistry.cs | Maps tenant IDs to connection strings |
PowerSeller.SaaS.Infrastructure/Data/DesignTimeDbContextFactory.cs | EF Core migration tooling support |
PowerSeller.SaaS.Infrastructure/MultiTenancy/TenantContext.cs | Scoped tenant state |
PowerSeller.SaaS.Modules.BestEx/BestExModule.cs | Module shell with /api/bestex/status endpoint |
Dockerfile.dev | SDK-based dev image with dotnet watch and NuGet restore caching |
docker-compose.yml | Updated api service to use Dockerfile.dev |
Verification
docker compose --profile dev upstarts proxy + docs + api + cache (4 containers)http://pssaas.powerseller.local/api/health→ 200http://pssaas.powerseller.local/api/bestex/status→{"module":"BestEx","status":"ready"}http://pssaas.powerseller.local/api/swagger/index.html→ 200- BestEx module test passes (1 passed, 0 failed)
Issues Encountered
Directory.Build.propsmust be explicitly copied in the Dockerfile beforedotnet restore(not just*.sln)- Class library projects need
<FrameworkReference Include="Microsoft.AspNetCore.App" />to use Minimal API types WithOpenApi()requires a separate NuGet package -- removed for now, Swagger at host level is sufficient- Test adapter discovery fails when
bin/objdirectories are stale from different container runs -- clean first - Running
dotnet testinside the API container OOMs -- run tests in a separate container instance
Technical Debt
pssaas-webcontainer is in restart loop (no frontend code exists yet). Harmless but noisy in logs.- Test package versions are pinned to specific versions (2.9.2, 2.8.2, 17.11.1) rather than floating -- intentional for reproducibility.
- The
TenantMiddlewarefalls back to a "default" tenant when no tenant header/claim is present. This is for dev convenience; production must require explicit tenant identification.
What's Next
- Product Owner reviews BestEx spec (Draft status)
- After approval, begin BestEx implementation in
PowerSeller.SaaS.Modules.BestEx