4.5 KiB
4.5 KiB
Musify: backend
.NET 10 solution (Musify.slnx): Core/ (Domain, Application, Infrastructure), Hosts/ (Api, Worker, StreamingGateway) and Tests/. Architecture and flows are in ../docs/.
Commands
Run from backend/.
dotnet build Musify.slnx # warnings are errors, including unused usings (IDE0005)
dotnet test Musify.slnx # Infrastructure and Api tests need Docker (Testcontainers)
dotnet format Musify.slnx --verify-no-changes
- Read the totals of
dotnet test(total:,succeeded:) before trusting it; don't chain it with&&into a commit. - EF migrations:
dotnet ef migrations add <Name> --project Core/Musify.Infrastructure --startup-project Core/Musify.Infrastructure(setDatabase__ConnectionStringto any value). A model change needs a migration; the entity model lives inModelConfiguration.Apply, which the in-memory test database reuses. - An HTTP contract change (routes, DTOs, responses) needs the web-player schema regenerated (
web-player/src/lib/api/schema.d.ts, from/openapi/v1.json) andnpm run checkclean. - A MassTransit message change (events, routing slip arguments, process kinds) means the queues must be drained before deploying; note it in the commit.
Style
- Block-scoped namespaces (
namespace X { }; file-scopednamespace X;is not allowed), usings outside the namespace and sorted (.editorconfigenforces it). - Private fields camelCase without underscore, constants and
static readonlyfields PascalCase, explicit accessibility. ArgumentNullException.ThrowIfNull(x)withoutnameof.- Use
PlayList(notPlaylist) in code; configuration POCOs end inConfiguration; injected services are named after their type (storageService). - User-facing strings are in English (the web-player owns translation).
- Trim stored names; normalized columns go through
TextNormalizer.Normalize. - Format ids and numbers with
CultureInfo.InvariantCulture; putCancellationTokenlast.
Application layer
- One file per use case,
<Feature>/<UseCase>.cs, holding the record and its handler; responses go inResponses/. - Errors come from
AppErrors(NotFound,Forbidden= 403 for "not the owner",Conflict).Error.Unauthorized(401) only means "not signed in". The success value isResult.Success; the API maps it to 204. - Handlers that cannot fail return plain types. Lists are paginated through
PageRequest/ToPaginatedAsync, ordered with anIdtiebreak, and public reads return onlyLifeCycleStatus.Activerows. - Owned-entity operations go through
FindOwnedAsync(non-active entities are not found). - No try/catch around
SaveChangesAsyncorPublishAsyncin handlers: the outbox makes publish + save atomic. Infrastructure either handles an error or lets it propagate; never log and rethrow. - List query records keep
int PageNumber, int PageSize; the API binds them throughPageQueryand validates them withPageQueryValidator.
API layer
- Routes:
{id:guid}for the resource,{trackId:guid}for sub-resources,{id:long}/{userId:long}for user ids. - Every endpoint returns
IResult(Results.Okwhen the handler cannot fail,ToHttpResult()otherwise). - Validation limits live in
Validators/Limits.cs; the database column length wins when a limit disagrees with the web-player. Upload allow-lists are inValidators/Uploads.cs. .WithSummary(...)is sentence case without a trailing period.
Infrastructure and hosts
- MassTransit routing slips are built from
RoutingSlips.Create/AddStep/TrackFaults; activities for the three owner types share generic bases (MarkLifeCycleActivity<T>,DeleteEntityActivity<T>,UpdatePicturesActivity<T>). - Recurring work is Hangfire (
RecurringJobsRegistrar); intervals are in seconds and rounded up to whole minutes. - The Worker calls
AddApplicationand validates its service container on build, so any handler dependency must be registered there. Shared config sections in itsappsettings.jsonmirror the API's. - The StreamingGateway references neither Application nor Infrastructure and keeps its own options.
Tests
- xUnit v3; pass
TestContext.Current.CancellationTokento methods that take a token (analyzer xUnit1051). - Test names are
Method_Scenario_Expected. - Application tests use the in-memory SQLite
TestDatabase; Infrastructure and Api tests use Testcontainers (Postgres, SeaweedFS). MassTransit sagas and consumers are tested on the MassTransit test harness. Infrastructureexposes its internals toMusify.Infrastructure.Tests.