-
Notifications
You must be signed in to change notification settings - Fork 1
Common Library
TrackHubCommon is the shared foundation every backend service builds on. It ships as four local NuGet packages, not project references:
| Package | Contents |
|---|---|
TrackHubCommon.Domain |
Constants, enums, cryptography extensions, localization, domain-event primitives |
TrackHubCommon.Application |
The mediator, the behavior pipeline, attributes, testing helpers |
TrackHubCommon.Infrastructure |
EF conventions and interceptors, the GraphQL client factory, IdentityService
|
TrackHubCommon.Web |
GraphQL server registration, error filters, security scheme transformers |
The structure follows Jason Taylor's Clean Architecture template, adapted to TrackHub's needs.
Related pages: Architecture · Inter-Service Communication
| Area | What it provides |
|---|---|
| Constants |
Resources, Actions, Roles, Policies, Clients, FeatureKeys, BackgroundJobKeys, Reports, SchemaMetadata, TableMetadata, ColumnMetadata, ViewMetadata, GroupMetadata
|
| Enums |
ProtocolType, AccountStatus, AnnouncementSeverity, and the rest of the cross-service enums |
| Cryptography | BCrypt for user and driver passwords; server-certificate encryption for third-party secrets such as GPS provider credentials. Two mechanisms, chosen per data type. |
| Localization |
ResourceLocalizer — GetString(key) against the ambient request culture, GetString(key, locale) for background or per-recipient rendering, and NormalizeLanguage("es-CO") → "es"
|
| Json | UTC DateTimeOffset converters |
| Events |
BaseEvent, BaseAuditableEntity, AddDomainEvent()
|
The constant catalogs are the contract. Resource, action, feature-key, schema and table names are never string literals at a call site — a typo there becomes a silent authorization or mapping failure. Adding a constant means repacking (see below).
Common.Mediator is TrackHub's own CQRS dispatcher — MediatR is not used and is forbidden. It provides IRequest<TResult> / IRequest, IRequestHandler<,>, MediatorDispatcher : ISender, IPublisher, and Unit.
Registered by AddApplicationServices(assembly). AddBasicApplicationServices registers validation and exception handling only, for hosts that do not need the full chain.
| Behavior | Role |
|---|---|
LoggingBehavior |
Request lifecycle at Debug; caller name resolution gated on logger.IsEnabled(Debug) and on the principal being a user |
ValidationBehavior / GraphQLValidationBehavior
|
FluentValidation |
AuthorizationBehavior |
[Authorize] enforcement through IIdentityService
|
AccountScopeBehavior |
Fail-closed multi-tenant isolation |
CachingBehavior |
[Caching] over IDistributedCache
|
RateLimitingBehavior |
[RateLimiting] token bucket |
UnhandledExceptionBehavior |
Catch-all logging |
See Architecture for the ordering and the rules each one enforces.
Common.Application.Testing.AccountScopeCoverage is wired as AccountScopeCoverageTests in every service's Application.UnitTests project. It fails the build when a request type is neither account-bearing nor explicitly marked, and when [Caching] is combined with a caller-scoped or handler-enforced request.
The gate only sees wire keys. Keyless service-called surfaces are invisible to it and must be checked by enumerating the consumers' GraphQL documents. See Inter-Service Communication.
| Component | Role |
|---|---|
AuditableEntityInterceptor |
Maintains created/modified columns automatically |
DispatchDomainEventsInterceptor |
Publishes domain events after successful persistence |
UseUtcTimestamps() |
The EF convention that keeps every timestamp column timestamp with time zone
|
IGraphQLClientFactory |
The single registration path for every inter-service client — timeouts, header propagation, resilience |
IdentityService |
Centralized identity and permission checks against the Security API, with the 30 s decision cache |
AddTrackHubGraphQLServer<TQuery, TMutation>(isDevelopment) is the single definition of the platform's GraphQL server configuration: authorization, AddMaxExecutionDepthRule(15), TrackHubGraphQLErrorFilter, request options, and the query/mutation types. Per-service extras chain onto the returned builder.
TrackHubGraphQLErrorFilter maps the platform's typed exceptions onto stable GraphQL error codes — see Architecture.
TrackHubCommon/Directory.Build.props is the source of truth for the version, applied in lockstep to all four packages. The current version is 1.0.12.
Every consumer tracks the current version:
- the eight service repositories — Manager, Telemetry, Security, Router, Geofencing, Reporting, AuthorityServer, TripManagement — through their
Directory.Packages.props; -
the ServiceContracts harness, through a direct
PackageReferenceinTrackHub.ServiceContracts.Harness.csproj. It has noDirectory.Packages.props, so a props-only sweep misses it and the contract suite then fails to restore.
Bump every consumer together, and never pin one back.
When any TrackHubCommon.* project changes — a new constant, a new behavior, a contract change — the local packages must be rebuilt and every consumer bumped.
-
Bump
<Version>inTrackHubCommon/Directory.Build.props. -
Build, do not pack. The projects set
GeneratePackageOnBuild=true, so packages are produced duringdotnet build.dotnet packcan package a stale DLL, or fail with NU5026 after a clean — it does not reliably recompile. -
Copy the
.nupkgfiles from eachsrc/Common.*/bin/Debug/to the local feed and toTrackHubCommon/NugetPackages/. -
Purge the global cache when repacking the same version:
Otherwise consumers restore the previously extracted copy and you get confusing
rm -rf ~/.nuget/packages/trackhubcommon.*/<version>
CS0117 'Resources' does not contain …errors against code you just wrote. - Bump and restore every consumer, including the ServiceContracts harness.
- If the change added an authorization resource, also add it to
TrackHubSecurity'sApplicationDbContextInitializerDefaultResources— and remember that adding a resource-action row and granting it to a role are two separate steps.
Docker image builds pack the packages automatically in a
commonstage, so a deployment does not need a pre-populated feed. A localdotnet efrun does — see Deployment and Operations.
-
Common.Domain.Enumsmust not be added to Manager's InfrastructureGlobalUsings— itsTransporterTypecollides with theInfrastructure.Entitiestable entity of the same name. Import it per file there. -
Common's defaultIFeatureFlagServiceis fail-open (AlwaysEnabledFeatureFlagService,TryAddScoped). A service that does not register its own silently ignores every[RequireFeature]. -
Common'sAccountScopeBehavioris fail-closed. The two defaults pull in opposite directions on purpose: a missing tenant scope is a security failure, a missing feature registration is a service-configuration failure that its own tests should catch. - Verifying that a constant landed in a built DLL is best done with
grep -a;stringsis not always available, and UTF-16 metadata literals defeat it anyway.
Platform
- Architecture
- Technology
- Inter-Service Communication
- Database
- Security and Identity
- User Permissions Overview
Services
- Manager
- Telemetry
- Router
- Adding a Provider
- Geofencing
- Trip Management
- Reporting
- Common Library
- Frontend
Engineering
User docs
- User Guide (ships in the app)