An application built using .NET 8 and following a Domain-Driven Design approach by using the BridgingIT DevKit.
- Application Commands/Queries
- Domain Model, ValueObjects, Events, Rules, TypedIds, Repositories
- Presentation Model
- Unit & Integration Tests
The supporting containers should first be started with docker-compose up
or docker-compose up -d
.
Then the Presentation.Web.Server project can be set as the startup project.
On CTRL+F5
this will start the host at https://localhost:7144.
- SQL Server details:
Server=127.0.0.1,14339;Database=bit_devkit_gettingstarted;User=sa;Password=Abcd1234!;Trusted_Connection=False;TrustServerCertificate=True;MultipleActiveResultSets=true;encrypt=false;
- Swagger UI is available here.
- Seq Dashboard is available here.
The GettingStarted project, powered by bITDevKit, is structured around key architectural layers:
Contains commands, queries, and their respective handlers.
public class CustomerCreateCommand
: CommandRequestBase<Customer>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override ValidationResult Validate() =>
new Validator().Validate(this);
public class Validator : AbstractValidator<CustomerCreateCommand>
{
public Validator()
{
this.RuleFor(c => c.FirstName).NotNull().NotEmpty().WithMessage("Must not be empty.");
this.RuleFor(c => c.LastName).NotNull().NotEmpty().WithMessage("Must not be empty.");
}
}
}
(CustomerCreateCommandHandler.cs)
public class CustomerCreateCommandHandler
: CommandHandlerBase<CustomerCreateCommand, Customer>
{
private readonly IGenericRepository<Customer> repository;
public CustomerCreateCommandHandler(
ILoggerFactory loggerFactory,
IGenericRepository<Customer> repository)
: base(loggerFactory)
{
this.repository = repository;
}
public override async Task<CommandResponse<Customer>> Process(
CustomerCreateCommand request,
CancellationToken cancellationToken)
{
var customer = new Customer { FirstName = request.FirstName, LastName = request.LastName };
await this.repository.UpsertAsync(customer, cancellationToken).AnyContext();
return new CommandResponse<Customer> // TODO: use .For?
{
Result = customer
};
}
}
Defining your core business logic with domain models and aggregates.
public class Customer : AggregateRoot<Guid>
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Providing the backbone with a DbContext setup and repository implementations.
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
Serves as the entry point for external interactions, focusing on delivering data and services to clients.
builder.Services.AddCommands();
builder.Services.AddQueries();
builder.Services
.AddSqlServerDbContext<CoreDbContext>(o => o
.UseConnectionString(builder.Configuration.GetConnectionString("Default")))
.WithDatabaseMigratorService();
public class CustomerViewModel
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
[ApiController]
[Route("api/[controller]")]
public class CustomersController : ControllerBase
{
private readonly IMediator mediator;
public CustomersController(IMediator mediator)
{
this.mediator = mediator;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<CustomerViewModel>>> GetAsync()
{
var query = new CustomerFindAllQuery();
var result = await this.mediator.Send(query);
return this.Ok(result?.Result?.Select(e =>
new CustomerViewModel
{
Id = e.Id.ToString(),
FirstName = e.FirstName,
LastName = e.LastName
}));
}
[HttpPost]
public async Task<ActionResult> PostAsync([FromBody] CustomerViewModel model)
{
if (model is null)
{
return this.BadRequest();
}
var command = new CustomerCreateCommand()
{
FirstName = model.FirstName,
LastName = model.LastName
};
var result = await this.mediator.Send(command);
return this.Created($"/api/customers/{result.Result.Id}", null);
}
}
Ensuring reliability through comprehensive unit, integration, and HTTP tests.
Start the application (CTRL-F5) and use the following UI to test the API:
Start the application (CTRL-F5) and use the following HTTP requests to test the API: API.http