mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 08:23:12 +00:00
* refactor(api): enforce service and data-service boundaries * refactor(api): complete feature service boundaries * refactor(identity): enforce service and data boundaries * refactor(vendors): enforce service and data boundaries * refactor(workorders): enforce service and data boundaries * refactor(backend): enforce architecture and optimize dispatch * style(backend): format changed architecture files * fix(architecture): address backend review follow-ups * fix(backend): sanitize exception disclosure in changed API endpoints Replace raw exception-message disclosure (ex.Message) returned to API callers with a stable sanitized public message plus correlated structured internal logging, across the endpoints changed in this PR. - Add SanitizedErrors helper: logs the original exception at Error with a generated correlation id and returns a stable public message referencing it so support can trace without exposing internals. - Inject ILogger<T> into the 14 changed controllers and route every ex.Message/dbex.Message disclosure through the helper, preserving status codes, response shapes, and business data (e.g. OpenWorkOrders). - Leave FluentValidation (vex.Errors) and existing fixed-message catches untouched; out-of-scope controllers (Account/Contact/Employee/Asset/ PMSchedule) are unchanged. - Add focused tests proving internal exception text is not returned and that Error logging carrying the original exception is invoked. * fix(architecture): abstract job run state access * style: format board update service * test: use collection assertion idiom
106 lines
4.7 KiB
Markdown
106 lines
4.7 KiB
Markdown
# Backend Architecture and Quality Rules
|
|
|
|
This repository uses a feature-oriented service boundary over Entity Framework Core:
|
|
|
|
```text
|
|
HTTP controller -> I{Feature}Service -> I{Feature}DataService -> ApplicationDbContext
|
|
```
|
|
|
|
The interfaces are architectural seams, not a requirement to wrap every class or every
|
|
EF method. Add an interface when it separates HTTP, business, persistence, or external
|
|
I/O responsibilities and enables behavior-focused testing.
|
|
|
|
## Layer responsibilities
|
|
|
|
### API controllers
|
|
|
|
- Parse HTTP input, authorize the caller, invoke a business-service interface, and map
|
|
the result to the established HTTP contract.
|
|
- Do not inject `ApplicationDbContext`, concrete service implementations, or concrete
|
|
data-service implementations.
|
|
- Do not contain persistence queries or business workflows.
|
|
- Do not expose exception messages to clients. Map known failures explicitly and let the
|
|
centralized exception boundary handle unexpected failures.
|
|
|
|
### Business services
|
|
|
|
- Own validation, business decisions, orchestration, and transaction intent.
|
|
- Depend on interfaces for persistence and external I/O.
|
|
- Use `IOptions<T>` for typed configuration. Do not inject `IConfiguration`.
|
|
- Keep feature responsibilities cohesive. Split a service when it has independently
|
|
changing business reasons, not because it crossed an arbitrary line count.
|
|
- Preserve existing API and board-backed behavior unless the accepted requirement
|
|
explicitly changes it.
|
|
|
|
### Data services
|
|
|
|
- Own EF Core query shape, persistence, batching, and transaction implementation.
|
|
- Accept cancellation tokens on new I/O methods and pass them to EF Core.
|
|
- Use `AsNoTracking()` for read-only queries.
|
|
- Project only required columns for list/read models; include full graphs only when the
|
|
caller needs them.
|
|
- Page unbounded collections at the database.
|
|
- Avoid query-in-loop and save-in-loop patterns. Prefer set-based reads and batched writes
|
|
while preserving the workflow's failure and transaction semantics.
|
|
- Do not introduce a generic repository over EF Core. Feature data services should expose
|
|
intent-revealing operations.
|
|
|
|
## Performance review
|
|
|
|
For each changed hot path, document and test:
|
|
|
|
- input size variables;
|
|
- CPU and memory complexity;
|
|
- database round trips;
|
|
- whether filtering, ordering, and paging execute in SQL;
|
|
- whether tracking is necessary;
|
|
- whether repeated work can be hoisted out of loops;
|
|
- the partial-failure and transaction behavior.
|
|
|
|
Prefer dictionary or hash-set reconstruction when database results must follow caller
|
|
order. A query followed by dictionary reconstruction is normally `O(n)` CPU and memory;
|
|
repeated `First`/`Single` scans over the result are `O(n²)`.
|
|
|
|
Do not optimize by weakening correctness. In particular, batching all writes into one
|
|
final save can change partial-success behavior, and adding a transaction can change
|
|
locking and retry semantics. Those are business decisions, not mechanical cleanup.
|
|
|
|
## Configuration
|
|
|
|
Configuration is bound once during composition in
|
|
`SeaHaven.Services/DependencyInjection/ServicesModule.cs`. Business services receive
|
|
typed options such as `FrontendOptions`, `JwtOptions`, `ApprovalsOptions`, and
|
|
`VendorPortalOptions`.
|
|
|
|
Defaults must preserve current behavior. Startup validation should be introduced only
|
|
when every deployed environment is known to provide the required value.
|
|
|
|
## Enforcement
|
|
|
|
`ArchitectureTests` enforces mechanical dependency rules:
|
|
|
|
- controllers cannot inject EF contexts or concrete service/data-service classes;
|
|
- business services cannot inject EF contexts or raw `IConfiguration`.
|
|
|
|
The pull-request quality workflow runs those tests and verifies formatting for changed
|
|
C# files. The normal CI workflow builds the solution and runs the full test suite.
|
|
|
|
Architecture tests are appropriate for dependency direction. Behavior belongs in tests
|
|
through public interfaces. Do not add tests merely to prove a linter or analyzer itself
|
|
works; test the product behavior the rule protects.
|
|
|
|
## Review checklist
|
|
|
|
- Controller depends only on service abstractions.
|
|
- Business logic is in a cohesive feature service.
|
|
- EF operations are behind a feature data-service abstraction.
|
|
- No raw `IConfiguration` in a business service.
|
|
- No unbounded load followed by in-memory filtering.
|
|
- No query/save loop where a set-based operation preserves semantics.
|
|
- Read-only EF queries use no tracking.
|
|
- New async I/O accepts and propagates cancellation where the public contract permits.
|
|
- Errors do not leak internal exception details.
|
|
- Tests cover behavior, authorization, data contracts, ordering, duplicates, missing
|
|
records, and relevant failure boundaries.
|
|
- Changed behavior has been checked against the authoritative ticket board. Missing board
|
|
access blocks readiness and merge claims.
|