12 - How-To Guides
Recipes for common backend dev tasks.
How to Add a New Controller
- Create a file in
backend/src/MO.Ekyc.Api/Controllers/StageN/(orCommon/,Admin/) - Decorate with
[ApiController],[Route(...)]and optionally[RequiresStage(N)] - Inject the service you need via constructor
- Delegate to the service - keep the action thin
- Wrap the result in
ApiResponse<T>with aJourneyStatus - Add a route constant to
MO.Ekyc.Shared/Constants/ApiRoutes.cs
backend/src/MO.Ekyc.Api/Controllers/StageN/MyController.csC#
[ApiController]
[RequiresStage(5)]
public class MyController : ControllerBase
{
private readonly MyService _service;
public MyController(MyService service) { _service = service; }
[HttpPost(ApiRoutes.MyEndpoint)]
public async Task<IActionResult> DoSomething([FromBody] MyCommand cmd, CancellationToken ct)
{
var result = await _service.ExecuteAsync(cmd, ct);
return Ok(ApiResponse<MyResult>.Ok(result));
}
}
How to Add a New Service
- Create a file in
backend/src/MO.Ekyc.Infrastructure/Services/StageN/ - Inject dependencies:
EkycDbContext,ILogger<T>,IJourneyTracker, configuration - Write methods that take Command DTOs and return Result DTOs
- Use
_db.SaveChangesAsync(ct)to persist - Call
_journeyTracker.RecordStateTransitionAsyncfor state changes - Register in
Program.cs:builder.Services.AddScoped<MyService>();
How to Add a New External Provider
- Define or locate the interface (e.g.,
IPanProvider) inInfrastructure/ExternalProviders/Pan/ - Create the provider class implementing that interface +
IExternalProvider - Inject
IHttpClientFactory,IConfiguration,ILogger<T>,ApiAuditLogger - Add a named HttpClient in
Program.cs:builder.Services.AddHttpClient("NewProvider", c => { c.BaseAddress = ...; c.Timeout = ...; }); - Register the provider:
builder.Services.AddScoped<IPanProvider, NewProvider>(); - Insert a row into
provider_configurationswith category=PAN, priority, enabled=true, mock_mode - Log every call via
_auditLogger.LogAsync(...)for audit trail
How to Add a New Domain Entity
- Create a POCO in
backend/src/MO.Ekyc.Domain/Entities/MyEntity.cs- no dependencies, just fields - Add a
DbSet<MyEntity>toEkycDbContext - Create
backend/src/MO.Ekyc.Infrastructure/Persistence/Configurations/MyEntityConfiguration.csimplementingIEntityTypeConfiguration<MyEntity>for indexes, foreign keys, column types - Run a migration:
dotnet ef migrations add AddMyEntity -p MO.Ekyc.Infrastructure -s MO.Ekyc.Api - Apply:
dotnet ef database update
How to Create an EF Core Migration
BashMigration commands
# Install EF tools if not already
dotnet tool install --global dotnet-ef --version 8.0.*
# Add a new migration
cd D:\MO_Project\ekyc\backend
dotnet ef migrations add InitialCreate \
--project src\MO.Ekyc.Infrastructure \
--startup-project src\MO.Ekyc.Api \
--output-dir Persistence\Migrations
# Apply migrations to DB
dotnet ef database update \
--project src\MO.Ekyc.Infrastructure \
--startup-project src\MO.Ekyc.Api
# Generate SQL script (for prod DBA review)
dotnet ef migrations script \
--project src\MO.Ekyc.Infrastructure \
--startup-project src\MO.Ekyc.Api \
--output schema.sql
How to Add a Hangfire Background Job
C#Enqueue a fire-and-forget job
using Hangfire;
public class MyController : ControllerBase
{
private readonly IBackgroundJobClient _jobClient;
public MyController(IBackgroundJobClient jobClient) { _jobClient = jobClient; }
[HttpPost("do-async")]
public IActionResult DoAsync(MyCommand cmd)
{
// Fire and forget
_jobClient.Enqueue<MyBackgroundJob>(job => job.RunAsync(cmd.LeadId));
// Scheduled
_jobClient.Schedule<MyBackgroundJob>(
job => job.RunAsync(cmd.LeadId),
TimeSpan.FromMinutes(5));
return Ok();
}
}
public class MyBackgroundJob
{
private readonly EkycDbContext _db;
public MyBackgroundJob(EkycDbContext db) { _db = db; }
public async Task RunAsync(Guid leadId)
{
// work happens here, can use injected services
}
}
How to Add a New Configuration Section
- Add the JSON section to
appsettings.jsonand/orappsettings.Development.json - Create a strongly-typed options class:
public class MyOptions { public string BaseUrl { get; set; } = ""; } - Bind in
Program.cs:builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("MySection")); - Inject
IOptions<MyOptions>where needed
How to Publish a Downstream Event
C#Publish to Kafka / downstream consumers
public class MyService
{
private readonly IDownstreamEventPublisher _publisher;
public MyService(IDownstreamEventPublisher publisher) { _publisher = publisher; }
public async Task SomethingHappenedAsync(Guid leadId, ...)
{
// Publish event to configured targets (CLEVERTAP, CDP, DATALAKE, ZOHO_CRM)
await _publisher.PublishAsync(
eventType: "SOMETHING_HAPPENED",
leadId: leadId,
targets: new[] { "CLEVERTAP", "CDP" },
ct: ct);
}
}
How to Populate a Sync Table Locally
Some tests require reference data. Use the SQL scripts from the BRD Analysis folder as seed data.
- See DB Actions Checklist for the list of seed scripts needed
- Typical pattern:
psql -U postgres -d ekyc3 -f seed_bank_ifsc.sql - For one-time populate tables (pincodes, states, cities), use data from the old DB or BRD docs