Robust .NET BFF (Backend for Frontend) development template at IATec, promoting standard practices, efficiency and security. Ideal for scalable, high-performance APIs acting as an orchestration layer for frontend clients.
Current Version: 2.1.0
- About the Project
- Technologies and Stack
- Project Structure
- Architecture Layers
- Prerequisites
- How to Run
- Configuration
- API Documentation (Scalar)
- Health Checks
- API Versioning
- CORS
- Authentication
- Feature: Assets
- Logging Dispatcher
- External Services (AntiCorruption)
- Persistence Layer
- Domain Layer
- Message Queue Layer
- CrossCutting Layer
- Test Projects
- Docker
- CI/CD
- Renaming the API
- Template Extension Points
- Contributing
- Changelog
This repository is a base template / scaffolding project for creating new .NET BFF (Backend for Frontend) services following IATec standards.
In the BFF pattern, the frontend knows only this API — it never calls downstream services directly. This API is responsible for receiving all frontend requests, calling whichever internal microservices or external APIs are needed, aggregating the results, and returning a single response. This keeps the frontend decoupled from the backend topology and centralizes cross-cutting concerns (auth, logging, error handling) in one place.
- Clean Architecture / Vertical Slices with MediatR CQRS.
- API Versioning via
Asp.Versioning.Mvc(query stringapi-version). - Scalar interactive API documentation (replaces Swagger).
- Health Checks with version endpoint.
- CORS policy (
AllowAnyOrigin,AllowAnyMethod,AllowAnyHeader). - FluentValidation pipeline behavior (validators run before handlers).
- Exception pipeline behavior via
IATec.Shared.Behaviors. - Logging dispatcher sending structured logs to IATec Log Service.
- Typed HttpClient for external IATec Log Service integration.
- Integration with internal
IATec.Shared.*packages.
- All command/query handlers in
Application/Features/Assets/throwNotImplementedException. Persistencelayer is empty — BFF pattern typically has no direct database access.Domainproject contains zero.csfiles.CrossCuttingproject contains zero.csfiles.MessageQueueDependencyInjectionConfigis empty.docker/Dockerfileanddocker/Local.Dockerfileare empty (0 bytes).- Test projects have no test framework packages (xUnit, NUnit, MSTest) and zero tests.
- No CI/CD pipelines (no
.github/workflows).
Note: Whenever creating a new BFF from this template, read the Renaming the API section to adjust names and references.
| Technology | Version |
|---|---|
| .NET | 10.0 |
| ASP.NET Core | 10.0.8 |
| Scalar.AspNetCore | 2.14.14 |
| Microsoft.AspNetCore.OpenApi | 10.0.8 |
| API Versioning (Asp.Versioning.Mvc) | 10.0.0 |
| Asp.Versioning.Mvc.ApiExplorer | 10.0.0 |
| AspNetCore.HealthChecks.UI.Client | 9.0.0 |
| MediatR | 14.1.0 |
| FluentValidation | 12.1.1 |
| FluentValidation.DependencyInjectionExtensions | 12.1.1 |
| FluentResults | 4.0.0 |
| Microsoft.Extensions.Configuration | 10.0.8 |
| Microsoft.Extensions.Configuration.Binder | 10.0.8 |
| Microsoft.Extensions.Http | 10.0.8 |
| Microsoft.Extensions.Options | 10.0.8 |
| IATec.Shared.Api | 1.2.0 |
| IATec.Shared.Application | 2.0.0 |
| IATec.Shared.Domain | 2.0.1 |
| IATec.Shared.HttpClient | 3.0.0 |
| IATec.Shared.Behaviors | 1.3.0 |
CSPROJ Settings:
Nullable=enable,ImplicitUsings=enable,InvariantGlobalization=false,GenerateDocumentationFile=True.
IATec.Standard.Net.Bff/
├── src/
│ ├── Api/
│ │ ├── Configurations/
│ │ │ ├── ApiDependencyInjectionConfig.cs
│ │ │ └── Extensions/
│ │ │ ├── CorsPolicyExtension.cs
│ │ │ ├── HealthCheckExtension.cs
│ │ │ ├── MigrationExtensions.cs
│ │ │ ├── OptionsExtension.cs
│ │ │ ├── ScalarConfiguration.cs
│ │ │ └── VersioningExtension.cs
│ │ ├── Controllers/
│ │ │ └── (empty folder)
│ │ ├── Properties/
│ │ │ └── launchSettings.json
│ │ ├── appsettings.json
│ │ └── Program.cs
│ │
│ ├── Application/
│ │ ├── Configurations/
│ │ │ ├── ApplicationDependencyInjectionConfig.cs
│ │ │ ├── Extensions/
│ │ │ │ ├── MediatorConfig.cs
│ │ │ │ └── ValidatorConfig.cs
│ │ │ └── Factories/
│ │ │ └── ValidatorFactory.cs
│ │ ├── Dispatchers/
│ │ │ └── Logging/
│ │ │ └── LogDispatcher.cs
│ │ └── Features/
│ │ └── Assets/
│ │ ├── Commands/
│ │ │ ├── CreateAssetCommand.cs
│ │ │ └── CreateAssetCommandHandler.cs
│ │ ├── Queries/
│ │ │ ├── CheckIfExistsAssetQuery.cs
│ │ │ └── CheckIfExistsAssetQueryHandler.cs
│ │ └── Validators/
│ │ └── CreateAssetValidator.cs
│ │
│ ├── CrossCutting/
│ │ └── (empty — no .cs files)
│ │
│ ├── Domain/
│ │ └── (empty — no .cs files)
│ │
│ ├── Persistence/
│ │ ├── Configurations/
│ │ │ ├── PersistenceDependencyInjectionConfig.cs
│ │ │ └── Extensions/
│ │ │ └── DatabaseExtension.cs
│ │ └── (empty — BFF pattern typically has no direct DB access)
│ │
│ ├── AntiCorruption/
│ │ ├── Configurations/
│ │ │ ├── AntiCorruptionDependencyInjectionConfig.cs
│ │ │ └── Extensions/
│ │ │ └── LoggingConfig.cs
│ │ └── Services/
│ │ └── Iatec/
│ │ └── LogService.cs
│ │
│ ├── MessageQueue/
│ │ └── Configurations/
│ │ └── MessageQueueDependencyInjectionConfig.cs
│ │
│ ├── Domain.Tests/
│ │ └── Domain.Tests.csproj (empty — no tests, no framework)
│ │
│ └── Application.Tests/
│ └── Application.Tests.csproj (empty — no tests, no framework)
│
├── docker/
│ ├── Dockerfile (empty — 0 bytes)
│ └── Local.Dockerfile (empty — 0 bytes)
│
├── secrets/
│ └── secrets.yml (Kubernetes Secret template)
│
├── IATec.Standard.Net.Bff.sln
├── README.md
└── CHANGELOG.md
Entrypoint ASP.NET Core Web API. Orchestrates all configurations and maps controllers.
Contains use cases, MediatR handlers, validators, dispatchers, and factories. In a BFF, handlers typically orchestrate calls to downstream services via AntiCorruption typed clients.
Mediator Pipeline Behaviors (from IATec.Shared.Behaviors):
ValidatorPipelineBehavior<,>— runs FluentValidation validators before the handler.ExceptionPipelineBehavior<,>— global exception handling wrapper.
Currently empty. In a BFF context, the Domain layer typically holds DTOs, response models, and contracts for downstream service calls rather than business entities.
Currently empty scaffolding. BFFs typically do not own a database. If caching or session storage is needed, add it here.
PersistenceDependencyInjectionConfig.cs— wires the layer.DatabaseExtension.cs— empty method body (public static void AddData(...) { }).
The primary extension point in a BFF. Add typed HttpClient services here to communicate with downstream microservices and APIs.
Currently implements:
- IATec Log Service (
LogService.cs) — typedHttpClientsendingLogDtoviaPOST v1/log.
Currently empty scaffolding. ConfigureMessageQueue() returns services with no registrations.
Currently empty. Contains only CrossCutting.csproj with references to IATec.Shared.Behaviors (1.3.0) and MediatR (14.1.0). Zero .cs source files.
- .NET 10 SDK
- (Optional) Docker for building/publishing images
- Editor of your choice (VS, VS Code, Rider)
git clone <repository-url>
cd {API_NAME}dotnet restoredotnet run --project src/Api/Api.csprojDefault profile (from launchSettings.json):
- URL:
http://localhost:5015 - Environment:
Local - Auto-opens browser at
/documentation
Settings are located in src/Api/appsettings.json.
| Section | Description | Example |
|---|---|---|
TimeZone |
Application time zone | "America/Sao_Paulo" |
Container.Name |
Deployment container metadata name | "MyBff-Production" |
Container.ContainerId |
Container identifier | "my-bff-container" |
Logging.LogLevel.Default |
ASP.NET Core log level | "Debug", "Information", "Warning" |
ConnectionStrings |
Optional cache/storage connection strings | "Redis": "localhost:6379" |
In OptionsExtension.cs:
| Option Class | Configuration Key | Purpose |
|---|---|---|
LogServiceOption |
LogServiceOption |
URL for IATec Log Service |
ContainerOption |
ContainerOption |
Container metadata for logging dispatcher |
Tip: Add downstream service URL options in
src/Api/Configurations/Extensions/OptionsExtension.csfor typed injection viaIOptions<T>.
There is no appsettings.Development.json file in the project. All configuration currently lives in the base appsettings.json.
The project uses Scalar (not Swagger) for interactive API documentation.
- OpenAPI JSON:
/openapi/v1.json - Scalar UI:
/documentation - Availability: Only in non-Production environments (
!app.Environment.IsProduction()). - Theme: Mars, dark mode, C# HttpClient as default client.
Endpoint: GET /_healthcheck/status
- VersionHealthCheck — returns
HealthStatus.Healthywith the assembly version string. - Response is formatted via
HealthChecks.UI.Client(rich JSON with UI metadata).
Configured in VersioningExtension.cs:
- Default version:
1.0 - Assume default when unspecified:
true - Report API versions:
true - Version reader: Query string parameter
api-version - Explorer group format:
'v'VVV(e.g.,v1)
Configured in CorsPolicyExtension.cs with AllowAnyHeader, AllowAnyMethod, AllowAnyOrigin.
Exposed headers: X-Custom-Header, Location, Content-Disposition, Content-Length.
⚠️ Warning:AllowAnyOrigin()is extremely permissive. Review and restrict before deploying to production.
Currently NOT configured.
JWT Bearer setup is not present. To add JWT authentication:
- Install
Microsoft.AspNetCore.Authentication.JwtBearer. - Configure token validation in
ApiDependencyInjectionConfig.csor a new extension method. - Add
app.UseAuthentication()beforeapp.UseAuthorization()inUseApi(). - Add security schemes to the OpenAPI document in
ScalarConfiguration.cs.
The only implemented feature domain in the Application layer. All handlers are placeholders (NotImplementedException).
| File | Type | Details |
|---|---|---|
CreateAssetCommand.cs |
readonly record struct |
IRequest<Result> from MediatR + FluentResults |
CreateAssetCommandHandler.cs |
Handler | throw new NotImplementedException() |
CheckIfExistsAssetQuery.cs |
readonly record struct |
IRequest<Result<bool>> |
CheckIfExistsAssetQueryHandler.cs |
Handler | throw new NotImplementedException() |
CreateAssetValidator.cs |
AbstractValidator<CreateAssetCommand> |
Empty — no rules defined |
Replace this feature with your own BFF orchestration features when cloning this template.
Implements ILogDispatcher (from IATec.Shared.Domain.Contracts.Dispatcher).
Constructs a LogDto with:
ContainerKey— fromContainerOption.KeySource,Owner,Action— caller-providedContent—object?.ToString() ?? string.EmptyDate—DateTime.UtcNow
Then delegates to ILogService.SendAsync().
LogService.cs (AntiCorruption layer):
- Typed
HttpClientwith base address fromLogServiceOption.Url. - Sends
POST v1/logas JSON. - Does NOT throw on failure — exceptions are caught and logged via
ILogger.LogError. Non-success HTTP responses are not treated as errors (noEnsureSuccessStatusCodecheck).
The AntiCorruption layer is the primary extension point for a BFF — add typed HttpClient services here for each downstream microservice or API.
| Config | File |
|---|---|
| DI Registration | LoggingConfig.cs |
| Implementation | LogService.cs |
| Contract | ILogService (from IATec.Shared.Domain) |
Status: Empty scaffolding. BFFs typically do not own a database directly.
PersistenceDependencyInjectionConfig.cs— wires the layer.DatabaseExtension.cs— empty method body (public static void AddData(...) { }).
If the BFF needs caching (Redis, MemoryCache) or session storage, implement it in this layer.
Status: Completely empty.
The src/Domain/ directory contains:
Domain.csprojwith NuGet referencesModels/folder containing only an.editorconfigfile- Zero
.cssource files
In a BFF context, this layer typically holds DTOs, response contracts, and aggregation models.
Status: Completely empty. ConfigureMessageQueue() returns services with no registrations. No message queue library referenced.
Status: Completely empty.
Contains only CrossCutting.csproj with references to IATec.Shared.Behaviors (1.3.0) and MediatR (14.1.0). Zero .cs source files.
| Project | Framework Packages | Test Files | Status |
|---|---|---|---|
Domain.Tests |
None | 0 | Empty |
Application.Tests |
None | 0 | Empty |
dotnet add src/Domain.Tests package xunit
dotnet add src/Domain.Tests package Microsoft.NET.Test.Sdk
dotnet add src/Domain.Tests package xunit.runner.visualstudioEmpty (0 bytes).
Empty (0 bytes).
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore "src/Api/Api.csproj"
RUN dotnet build "src/Api/Api.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "src/Api/Api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Api.dll"]Status: No CI/CD configured.
- No
.github/folder. - No GitHub Actions workflows.
- No Azure DevOps pipelines.
- No Jenkins/GitLab CI files.
The only file in secrets/ is secrets.yml, a Kubernetes Secret manifest template with placeholders (#{deployment_name}#, #{namespace}#).
Follow these steps when using this project as a base for a new BFF.
mv IATec.Standard.Net.Bff.sln {API_NAME}.slnUpdate Api.csproj if desired:
<AssemblyName>{API_NAME}.Api</AssemblyName>
<RootNamespace>{API_NAME}.Api</RootNamespace>Run a Replace All in src/ to adjust namespaces:
| From | To (example) |
|---|---|
namespace Api; |
namespace MyProject.Api; |
namespace Application; |
namespace MyProject.Application; |
namespace Persistence; |
namespace MyProject.Persistence; |
namespace Domain; |
namespace MyProject.Domain; |
Or keep simplified namespaces (Api, Application, Domain, etc.).
In src/Api/Configurations/Extensions/ScalarConfiguration.cs, replace {API_NAME} with the actual project name.
Replace all {API_NAME} placeholders with the actual project name.
Update <Version> in Api.csproj to 1.0.0 for the new project.
This project is an intentional scaffold/template. The following items are structural placeholders — not bugs or missing features. Each represents an extension point where a new BFF project should add its own implementation.
| # | Extension Point | Location | Purpose |
|---|---|---|---|
| 1 | NotImplementedException handlers |
Application/Features/Assets/ |
Example command/query structure — replace with BFF orchestration logic |
| 2 | Empty AntiCorruption services |
src/AntiCorruption/Services/ |
Add typed HttpClient services for each downstream microservice |
| 3 | Empty Domain layer |
src/Domain/ |
Add DTOs, response contracts, and aggregation models |
| 4 | Empty Persistence layer |
src/Persistence/ |
Add caching (Redis, MemoryCache) if needed |
| 5 | Empty MessageQueue layer |
src/MessageQueue/ |
Add producers/consumers if the BFF publishes events |
| 6 | Empty CrossCutting layer |
src/CrossCutting/ |
Add shared utilities, constants, or cross-cutting concerns |
| 7 | Empty test projects | *.Tests/ |
Add xUnit/NUnit/MSTest and write tests |
| 8 | No CI/CD | Root | Add GitHub Actions / Azure DevOps when ready |
| 9 | Empty Dockerfiles | docker/ |
Add build/publish steps for containerization |
| 10 | Permissive CORS | CorsPolicyExtension.cs |
Restrict origins/methods when deploying to production |
| 11 | No authentication | ApiDependencyInjectionConfig.cs |
Add JWT/Auth when security requirements are defined |
| 12 | {API_NAME} placeholders |
ScalarConfiguration.cs, README |
Rename when cloning template |
| 13 | No appsettings.Development.json |
src/Api/ |
Create environment-specific configs as needed |
Contributions are welcome! To contribute:
- Fork the repository.
- Create a branch:
git checkout -b feature/feature-name. - Commit with clear messages following Conventional Commits.
- Open a Pull Request for review.
See CHANGELOG.md for full release history.