Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ShohjahonSohibov/repo-for-agent/llms.txt

Use this file to discover all available pages before exploring further.

UpdaterAgent follows Clean Architecture with a strict inward-only dependency rule: outer layers call inner layers, and inner layers never reference anything outside themselves. This keeps domain logic free of infrastructure concerns, makes services independently testable, and lets external integrations be swapped without touching business rules.

Dependency flow

Dependencies flow in one direction only:
Api → Application → Infrastructure → Domain
  • Api depends on Application
  • Application depends on Infrastructure and Domain
  • Infrastructure depends on Domain
  • Domain has zero project dependencies
The Domain layer is the innermost ring and must never import from Application, Infrastructure, or Api. Any violation breaks the architecture’s testability guarantee.

Layer responsibilities

LayerProjectPurposeKey components
ApiUpdaterAgent.ApiHTTP handling, routing, auth enforcement, response formattingControllers, filters, SignalR hubs, middleware pipeline
ApplicationUpdaterAgent.ApplicationBusiness logic, orchestration, background jobsFeature services, Hangfire jobs, DTOs, Dependencies.cs
InfrastructureUpdaterAgent.InfrastructureDatabase access, external API brokers, file I/OAppDbContext, EF Core migrations, broker implementations
DomainUpdaterAgent.DomainCore entities, enums, value objects, result abstractionsEntity classes, ModelBase<TId>, AuditableModelBase<TId>, ITenantEntity

Service file convention

Every feature module in the Application layer follows a four-file layout:
FeatureName/
├── IFeatureService.cs          → Interface
├── FeatureService.cs           → Public methods (API surface)
├── FeatureService.Internals.cs → Private helpers (partial class)
└── Contracts/                  → Request and response DTOs
Large services split their implementation across FeatureService.cs (public methods) and FeatureService.Internals.cs (private helpers) using C# partial classes. This keeps individual files focused without splitting the service into multiple classes.

Result pattern

Services return Result<T> instead of throwing exceptions for expected business failures. This makes error paths explicit and avoids hidden control flow.
Result<T>.Success(data)
Result<T>.Failure(error)
// Check .IsSuccess before accessing .Data
Usage in a controller:
var result = await _loadService.GetLoadByIdAsync(id);
if (result.IsSuccess)
    return Ok(result.Value);

return NotFound(result.Error);
Result and Error are defined in UpdaterAgent.Domain/Abstractions/. Domain-specific errors are grouped by entity:
public static class LoadErrors
{
    public static Error NotFound(long id) =>
        new("Load.NotFound", $"Load with ID {id} was not found");

    public static Error InvalidStatus(LoadStatus current, LoadStatus target) =>
        new("Load.InvalidStatus", $"Cannot transition from {current} to {target}");
}
Throw exceptions only for truly exceptional, unrecoverable conditions — not for expected business failures like “not found” or “invalid input”.

Entity base classes

ClassProperties addedWhen to use
ModelBase<TId>Id, IsDeletedSimple entities without audit tracking
AuditableModelBase<TId>+ CreatedAt, UpdatedAt, CreatedBy, UpdatedByMost domain entities
ITenantEntityTenantId (long)Every entity that belongs to a tenant
New entities should inherit AuditableModelBase<long>, implement ITenantEntity, and add [Index(nameof(TenantId))] for query performance.

Dependency injection registration

1

Application services registered in Dependencies.cs

Open src/UpdaterAgent.Application/Dependencies.cs and call ConfigureApplicationServices(). All application services are registered as Scoped unless they are stateless singletons.
// src/UpdaterAgent.Application/Dependencies.cs
public static IServiceCollection ConfigureApplicationServices(
    this IServiceCollection services)
{
    services.AddScoped<ILoadService, LoadService>();
    services.AddScoped<IAuthService, AuthService>();
    // ...
    return services;
}
2

Infrastructure services registered in Dependencies.cs

Open src/UpdaterAgent.Infrastructure/Dependencies.cs and call ConfigureInfrastructureServices(). Register AppDbContext, brokers, and infrastructure helpers here.
// src/UpdaterAgent.Infrastructure/Dependencies.cs
public static IServiceCollection ConfigureInfrastructureServices(
    this IServiceCollection services, IConfiguration configuration)
{
    var dataSource = new NpgsqlDataSourceBuilder(connString)
        .EnableDynamicJson()
        .Build(); // Build once — NpgsqlDataSource is a singleton

    services.AddDbContext<AppDbContext>(options =>
        options.UseNpgsql(dataSource));

    services.AddScoped<IQuickManageBroker, QuickManageBroker>();
    // ...
    return services;
}
3

Both called from Program.cs

Program.cs in the Api layer calls both extension methods during startup, completing the full DI graph before the application starts handling requests.
The Infrastructure layer’s NpgsqlDataSource must be built once outside the AddDbContext options lambda. Building it per scope causes an ArgumentOutOfRangeException on the second request.

Build docs developers (and LLMs) love