You have been provided with a solution scaffold for a vehicle service booking system. The folder structure, project references, and database connection have been pre-configured for you.
Your task is to implement the business logic, data access, API endpoints, and tests across the existing layers.
- Project structure across three layers
DbContextwith connection stringProgram.cswith DI registration and middleware~Module.csfiles to register DI on individual layers
Implement the following models with their requirements as listed below:
Service Type
Id— Unique id for the typeName— Name of the typeDescription— Description of the typeDuration— Duration in minutes
Appointment
Id— Unique id for the appointmentCustomer Name— Name of the customerEmail— Email contact for the customerPhone— Phone contact for the customerVehicle VIN— VIN of the vehicle for serviceService Type ID— Relational Id for the service type of the bookingScheduled Date- Date Time of the booking
Implement the repository or repositories:
ServiceTypeRepository
GetAllAsync()— return all service typesGetByIdAsync(id)— return a single service type, ornullif not found
AppointmentRepository
GetAllAsync(page, pageSize)— return a paginated list of appointments, including the relatedServiceTypeGetByIdAsync(id)— return a single appointment including its relatedServiceType, ornullif not foundIsSlotTakenAsync(serviceTypeId, scheduledDateTime)— check if an active appointment already exists for this service type at the given date and timeCreateAsync(appointment)— create and return the new appointmentUpdateAsync(appointment)— update and return the appointmentDeleteAsync(id)— remove the appointment if found
or
Generic Repository
GetAllAsync(page, pageSize, includes, predicate?)— return a paginated list of entities and relational data from the table,predicateoptionalGetAsync(predicate, includes)— retrieve singular entity and relational data from tableAnyAsync(predicate)— check to see if entity exists using predicateAddAsync(predicate)— insert entity into tableUpdateAsync(entity)— update entity in tableDeleteAsync(entity)— delete entity from table
Once your models and repositories are implemented, run your initial migration:
dotnet ef migrations add InitialCreate --project ./VehicleServiceBooking.Data --startup-project ./VehicleServiceBooking.APIThe application will apply pending Migrations and seed data using the Data Seeder, if you want to apply it sooner you can use the command below:
dotnet ef database update --project ./VehicleServiceBooking.Data --startup-project ./VehicleServiceBooking.APIImplement Domain Transfer Objects for the Data Models:
Service Type DtoAppointment Dto
Implement the following service classes:
Whilst implementing your services, you should use libraries like AutoMapper to help make Mapping more consistent and configurable.
ServiceTypeService
GetAllAsync()— retrieve all service types and map toServiceTypeDtoGetByIdAsync(id)— retrieve by ID and map toServiceTypeDto
AppointmentService
-
CreateAsync(CreateAppointmentRequest request)must enforce the following business rules before persisting:Rule Detail VIN length Must be exactly 17 characters Business hours Must be Monday to Friday, 08:00–17:00 Hour slots Must be on an exact hour (e.g. 09:00, not 09:30) No double-booking Slot must not already be taken for the same service type -
UpdateAsync(int id, UpdateAppointmentRequest request)must apply the same validation rules asCreateAsync. -
CancelAsync(int id)— delegate to the repository and return the result.
You can use private helper methods for your validation logic
The endpoint empty classes have been templated, you will need to make the endpoints below:
-
ServiceTypeEndpoints.cs— create aGETendpoint with exception handling which catches401 Not Found -
AppointmentEndpoints.cs— create bothPOSTandPUTendpoints to allow Appointments to be made and updated, both endpoints should check for validation failes and return a400 Bad Requestresponse with a descriptive message.
The test class is scaffolded with the following cases — implement each one:
Either NUnit or Moq can be used
| Test | Description |
|---|---|
CreateAsync_ShouldThrow_WhenVINIsNot17Characters |
Invalid VIN should throw |
CreateAsync_ShouldThrow_WhenSlotIsOutsideBusinessHours |
7pm slot should throw |
CreateAsync_ShouldThrow_WhenSlotIsOnAWeekend |
Saturday slot should throw |
CreateAsync_ShouldThrow_WhenSlotIsAlreadyTaken |
Taken slot should throw |
CreateAsync_ShouldReturnAppointmentDto_WhenRequestIsValid |
Happy path |
CancelAsync_ShouldReturnFalse_WhenAppointmentDoesNotExist |
Missing ID |
CancelAsync_ShouldReturnTrue_WhenAppointmentExists |
Happy path |
Run the tests with:
dotnet testThe appsettings.Development.json has been setup with a default SQLite connection
string you can change this if you wish, then run using your IDE of choice or:
cd VehicleServiceBooking.API
dotnet runSwagger UI will be available at: