FrontDesk is your digital concierge service to help you manage all your vacation rental properties. From turning off the lights when no one is home to offering your guests premium local services on demand.
These validators should focus on ensuring data integrity and adherence to the database schema. This may include checking for referential integrity, unique constraints, and proper data types. They should be used in the repository layer when adding or updating entities to ensure that the data being saved to the database is valid and adheres to the schema.
In our project, we encountered a subtle circular dependency issue when using repository decorators with FluentValidation. This problem affected our update validators, where asynchronous rules (e.g., uniqueness checks) in our base validator were not being executed.
- Our repository is decorated with a validation layer that depends on an update validator.
- The update validator, in turn, depends on a base validator which needs to perform repository queries (like checking
for a unique
IntegrationId). - Because the repository injected into the base validator was the decorated (wrapped) version, a circular dependency was created. This cycle prevented the asynchronous rules in the base validator from firing properly.
As a temporary fix, we inject a repository delegate and create a fresh instance of the base validator on demand. This breaks the circular dependency at runtime and allows the base validator’s rules to execute as expected.
Example:
public class IntegrationProviderBaseValidator : AbstractValidator<IntegrationProvider>
{
public IntegrationProviderBaseValidator(Func<IGenericRepository<IntegrationProvider>> repositoryFactory)
{
RuleFor(provider => provider.IntegrationId)
.Cascade(CascadeMode.Continue)
.NotEmpty().WithMessage("IntegrationId is required.")
.MustAsync(async (provider, integrationId, cancellation) =>
{
var repository = repositoryFactory();
var result = await repository.ExistsAsync(x =>
x.IntegrationId == integrationId && x.Id != provider.Id);
if (result.IsFailure)
return false;
return !result.Value;
})
.WithMessage("IntegrationId must be unique.");
}
}This approach ensures that each time validation is performed, a fresh (unwrapped) repository instance is used, breaking the cycle.
A more robust solution is to move validation out of the repository layer entirely and into the service layer (or use a mediator pattern). This decouples validation from persistence, eliminates the circular dependency, and aligns with the separation of concerns principle.
This issue only surfaces when asynchronous rules (like uniqueness checks) are involved in a nested validator and the repository is decorated.
Developers should be aware of this gotcha when designing similar validation chains.
These validators should focus on enforcing business rules and ensuring the consistency of the data within the context of your application's business domain. They should be used in the service layer, where the business logic resides, to validate data before processing or passing it to the repository layer.
It is important to note that this project enforces UUID7 for all new models. This is to ensure that all models have a unique identifier that is sortable. This is especially important when dealing with large databases or when needing to sort data by creation date.
Therefore, when creating a new model, the model's ID will automatically be set to UUID7. If you have already set the Id property in the model, when you save the model, the ID will be automatically overriden with a new UUID7.
Generics can be used for any model to simplify setting up CRUD scaffolding.
To create a new end-to-end CRUD flow:
- Open a terminal and navigate to the solution's root directory.
- Run the following command:
Replace
dotnet scaffold --model <ModelName><ModelName>with the name of the model you want to create. This command will create a new folder with the model name in theFrontDeskproject and add the necessary files to set up a basic CRUD flow for the model.
This project includes scripts to extract TODO comments from the codebase and create corresponding GitHub issues. This helps track technical debt and ensures that TODOs aren't forgotten in the code.
# Navigate to the solution's root directory
cd /path/to/FrontDesk
# Run the script with required parameters
.\Scripts\Projects\ExtractTodos.ps1 -Owner "AweSamNet" -Repository "FrontDesk" -LabelName "todo" -RootDirectory "."# Navigate to the solution's root directory
cd /path/to/FrontDesk
# Run the script with required parameters
./Scripts/Projects/extract-todos.sh "AweSamNet" "FrontDesk" "todo" "."Both scripts will:
- Find all TODO comments in C# (.cs) and TypeScript (.ts) files
- Create GitHub issues for each TODO with appropriate labels
- Include file path and line number information in the issue
- Replace the TODO comments with issue references (
// Issue #{number}: {Issue Title}) - Avoid creating duplicate issues
Note: The scripts require the GitHub CLI (gh) to be installed and authenticated. Learn more at cli.github.com.
No licenses are offered.