The Legal API System is a modular monolithic .NET 9 web application built with Clean Architecture & DDD. It provides a foundation for legal document management and multi-domain business modules (current: Admin).
This codebase follows a Modular Monolith pattern: one deployable unit, multiple strongly isolated business modules.
Core characteristics:
- Explicit Module Enumeration:
ApplicationHelper.ModuleNameenum defines all module identities (e.g. ADMIN, future SHOP, CHAT) for routing, seeding, authorization scopes. - Vertical Slice Design: Each module owns its Commands, Queries, Parameter / Response models, validators, mappings, DbContext (current Admin). Cross-module chatter only through well-defined Shared contracts.
- Dynamic Handler Discovery: At startup assemblies are scanned; classes inheriting generic base types (e.g. ACommandHandler<,>, AQueryHandler<,>) are auto-registered. No manual endpoint wiring.
- Unified API Surface: Single WebApi host exposes per-module dynamic endpoints plus generic execution controllers. Module name is part of the URL path ensuring clarity and separation.
- Per-Module Data Concerns: Each module can introduce its own EF Core DbContext + migrations. MigrationService can run all or selected contexts via CLI switches (range / list / all).
- Shared Kernel Library:
Legal.Shared.SharedModelsupplies only cross-cutting primitives (DTOs, parameter models, validators) avoiding bidirectional dependencies between feature modules. - Encapsulated Infrastructure: Common infra (repositories, helpers, storage, directory utilities) lives under Service layer; modules consume via interfaces, enabling future extraction to microservices if needed.
- Consistent Naming & Discovery: Naming conventions (e.g. *ParameterModel, *ResponseModel, *Handler) allow reflection-based registration & endpoint generation.
- Feature Growth Path: New module can be added without altering existing module internals; only enum extension + DI scan + (optional) new migrations.
- Single Deployment Unit: Operational simplicity (one process/image) while preserving logical boundaries.
- Evolution Friendly: Clear seams enable later decomposition to services if scaling / independent deployment requirements emerge.
- Extend Module Enum: Add
SHOPtoApplicationHelper.ModuleName. - Create Application Project (if separate) or folder structure mirroring Admin (Commands, Queries, Validators, DbContext, Migrations).
- Implement DbContext (e.g.
ShopDbContext) and add EF configuration & migrations. - Add Command / Query handlers inheriting base handler abstractions; include corresponding Parameter / Response models (or reuse Shared ones).
- Register Assembly: Ensure new project referenced by WebApi; existing reflection registration picks up handlers automatically.
- Seed Data (optional): Provide JSON seed file path in API
InitializationOptionsfor module bootstrap. - Run MigrationService: Include new context (auto-discovered) or pass specific
--context-number. - Call Endpoints: Use dynamic endpoints
/api/{module}/commands/{Name}and/api/{module}/queries/{Name}where{module}=shop(case-insensitive mapping to enum).
- Reduced Coupling: Module boundaries enforced via separate folders / projects + limited shared surface.
- Faster Refactoring: Adding features confined to module vertical slice.
- Performance: In-process communication (no network penalty) while still structured.
- Operational Simplicity: Single build, single docker image, unified logging & tracing.
- Gradual Service Extraction: Any module can later be extracted with minimal churn due to isolation & explicit contracts.
Layers:
- API (
Legal.Api.WebApi) β HTTP endpoints, Swagger, auth pipeline - Application (
Legal.Application.Admin) β CQRS handlers, business rules, validation - Service (
Legal.Service.*) β Infrastructure concerns (EF, Repos, external services, helpers) - Shared (
Legal.Shared.SharedModel) β Cross-cutting models / DTOs / abstractions - Migration Service (
MigrationService) β MultiβDbContext migration & bootstrapping - Frontend (
Legal.Website.esproj) β SPA (Angular) website project integrated via Visual Studio JavaScript tooling
- Modular, pluggable module naming convention
- CQRS (segregated Command & Query endpoints with dynamic handler discovery)
- JWT auth & role-based authorization
- File upload (multipart) + file download pipeline
- PostgreSQL via EF Core (snake_case naming)
- Background data seeding (JSON driven, upsert style with optional update)
- Dockerized development & deployment workflow (CLI & Visual Studio)
- Centralized request execution abstraction
- Frontend SPA served via dedicated container / static host
Legal.Api.Solution/
βββ Api/Legal.Api.WebApi/
βββ Application/Legal.Application.Admin/
βββ Service/
β βββ Legal.Service.Infrastructure/
β βββ Legal.Service.Repository/
β βββ Legal.Service.Helper/
βββ Shared/Legal.Shared.SharedModel/
βββ Website/Legal.Website/
βββ OtherServices/MigrationService/
.NET 9, ASP.NET Core, EF Core (Npgsql), AutoMapper, FluentValidation, Newtonsoft.Json, SignalR, Swagger/OpenAPI, Docker, ImageSharp, FFMpegCore, Angular (Website), Nginx (container serve).
- .NET 9 SDK
- PostgreSQL 12+
- (Optional Recommended) Docker / Docker Compose
- VS 2022 / VS Code (VS recommended for integrated Docker + JS project)
The solution is configured for Visual Studio container tools using Docker Compose (see docker-compose.dcproj).
Steps:
- Install VS 2022 workloads:
- ASP.NET and web development
- .NET + Container Development Tools
- JavaScript/TypeScript (for Website project)
- Open solution (.sln). Visual Studio detects Docker support from:
- Api/Legal.Api.WebApi.csproj (DockerDefaultTargetOS, DockerfileContext)
- Website/Legal.Website/Legal.Website.esproj (JavaScript project with Docker metadata)
- docker-compose.dcproj at root.
- Set Startup Project: choose the docker-compose project in Solution Explorer (rightβclick Set as Startup). This launches all defined containers (API, Website, MigrationService, PostgreSQL, etc.).
- Configure Environment Variables: edit docker-compose.override.yml (or compose file) for:
- ConnectionStrings__Postgres
- ASPNETCORE_ENVIRONMENT=Development
- Press F5:
- Visual Studio builds container images (Fast mode if enabled) and starts containers.
- MigrationService runs first (if included) to apply migrations.
- Access:
- Frontend Website container (http://localhost:8080)
- API Swagger (http://localhost:4020/swagger)
- Debugging:
- API: Breakpoints work normally in .cs code.
- Website: Use browser dev tools; for live Angular development you can also run npm start outside container (Website project has StartupCommand npm start) and only keep API in Docker.
- Logs: Use View > Other Windows > Containers window in VS or run
docker compose logs -fexternally. - Hot Reload: .NET Hot Reload applies to the API when using Fast (mounted) mode; for Angular use its dev server outside Docker during active UI work.
git clone <repository-url>
cd Legal.Api.SolutionUse either appsettings.Development.json, user-secrets, or environment variables:
dotnet user-secrets set "ConnectionStrings:Postgres" "Host=localhost;Database=LegalDB;Username=postgres;Password=password;Port=5432"dotnet ef database update --project Application/Legal.Application.Admindotnet run --project OtherServices/MigrationService -- --update-database "y" --context-number "-1"Add InitializationOptions to Api appsettings (see Data Seeding section) and run the API once.
dotnet run --project Api/Legal.Api.WebApihttps://localhost:5001/swagger
Current:
- Admin: Users, Auth, Contract
Planned:
- Shop, Chat (scaffold-ready via enum expansion and handler assembly)
- JWT bearer tokens
- CORS configured via AllowedOrigins
- FluentValidation input enforcement
- Provider: PostgreSQL (Npgsql)
- Style: Code-first EF Core
- Migration execution:
- EF CLI (module specific)
- Central MigrationService (multi-context, discovery, command line flags)
# All contexts (auto apply)
dotnet run --project OtherServices/MigrationService -- --update-database "y" --context-number "-1"
# Specific contexts (comma or range)
--context-number "1" # single
--context-number "1,3" # multiple
--context-number "1-4" # rangeOptional overrides:
--data-db "Host=..." # override primary DB
A background service in the API reads InitializationOptions:
"InitializationOptions": [
{ "FilePath": "Seed/admin_users.json", "DoUpdate": true },
{ "FilePath": "Seed/roles.json", "DoUpdate": false }
]Each JSON file: array of objects. Records with new id are inserted; if DoUpdate=true existing ones are updated (excluding id). Timestamps (last_modified_time) are auto-updated. Place seed JSON files under a path copied to output (e.g., Api/Legal.Api.WebApi/Seed/...).
Command:
- POST /api/Command/Execute/{module}
- POST /api/Command/Upload/{module}
- GET /api/Command/ListAll/{module}
- GET /api/Command/Detail/{module}/{name} Query:
- POST /api/Query/Execute/{module}
- GET /api/Query/ListAll/{module}
- GET /api/Query/Detail/{module}/{name}
- POST /api/Query/download/{module} Public: Module-specific unauthenticated endpoints.
Environment Variables / User Secrets keys:
- ConnectionStrings:Postgres
- AllowedOrigins (CSV)
- MaxRequestBodySize (bytes)
Environment variable style for containers:
- ConnectionStrings__Postgres
- AllowedOrigins
docker compose up -d --buildAdjust env vars in compose file as needed. For Visual Studio workflow see earlier section.
Current test coverage focuses on the Admin Application layer.
Project:
- Tests/Legal.Application.Admin.Tests (xUnit, FluentAssertions, Moq, EFCore.InMemory, Coverlet)
Run all tests:
dotnet testCollect code coverage (lcov for report tools like ReportGenerator or Sonar):
dotnet test /p:CollectCoverage=true /p:CoverletOutput=../TestResults/ /p:CoverletOutputFormat=lcovNaming convention:
MethodUnderTest_Scenario_ExpectedResult
Examples (see source):
- RegistrationServiceTests.RegisterUserAsync_NewUser_PersistsAndReturnsDto
- RegistrationServiceTests.RegisterUserAsync_UserAlreadyExists_Throws
- ContractServiceTests.Get_ReturnsMappedDto_WhenFound
Testing patterns employed:
- Arrange/Act/Assert with clear separation
- Moq strict mocks to ensure only expected repository calls are made
- FluentAssertions for expressive assertions
- AutoMapper configured adβhoc per test class to keep mapper surface minimal
- Password hashing verified using helper (no direct implementation coupling)
Writing a new service/handler test:
- Configure minimal AutoMapper profile(s) for DTO <-> Entity
- Mock repository (IRepository / IDomainRepository) methods used by the handler/service
- Instantiate target service/handler directly (no full DI container required)
- Execute method under test with CancellationToken.None (or a token you can cancel for cancellation tests)
- Assert returned DTO shape & side effects (Verify mocks, verify password hashing, etc.)
- (Optional) Add negative path tests (validation failure, existing entity, not found, etc.)
Mapping validation (optional guard test):
var config = new MapperConfiguration(cfg => cfg.AddProfile<MyProfile>());
config.AssertConfigurationIsValid();Using EFCore.InMemory (for behaviour that depends on LINQ translation):
- Prefer to still mock repository unless you explicitly need query translation semantics.
- If used, seed context, execute, assert results.
Planned future test additions:
- Testcontainers PostgreSQL integration tests (real migrations + data seeding)
- Minimal API endpoint contract tests (Microsoft.AspNetCore.Mvc.Testing)
- Authentication / authorization pipeline tests
- Performance benchmarks for heavy queries
Quick watch mode (reruns on change) using dotnet-watch:
dotnet watch --project Tests/Legal.Application.Admin.Tests test- Backend architecture was originally designed and written by the author for a personal project and reused here (no AI assistance for backend implementation).
- All documentation content was generated with GitHub Copilot and reviewed/edited by the author.
- Frontend boilerplate was generated using GitHub Copilot and then manually updated/refined.
- Used AI to
- fixed Issue for dockization
- frontend unit tests
MIT License Β© Taufiq Abdur Rahman You may not use this codebase without permission. For Evolution purposes only.
Open an issue with reproduction steps & environment details.
- Api/Legal.Api.WebApi/README.md
- Application/Legal.Application.Admin/README.md
- Service/Legal.Service.Infrastructure/README.md
- Service/Legal.Service.Repository/README.md
- Service/Legal.Service.Helper/README.md
- Shared/Legal.Shared.SharedModel/README.md
- OtherServices/MigrationService/README.md