Skip to content

Dependency Decorator

This documentation aims to provide a clear and professional guide on using the Dependency decorator and the ValidateDependency function within the validation framework. Both tools are designed to offer powerful and flexible validation mechanisms, each serving specific roles in ensuring data integrity and business logic conformity.

Introduction

Dependency is a decorator used to specify validation rules for class properties based on the dependencies between them. It allows for dynamic validation where the validity of one property may depend on the value of another.

Usage

To use Dependency, annotate the target property with @Dependency, providing a validation configuration object. This object must define the name, getDependencies, validate, and message properties.

Example

Suppose we have a Product class where the saleDate must not precede the manufactureDate:

typescript
import { Dependency, ClassValidator } from 'rest-data-validator'

@ClassValidator
class Product {
  public manufactureDate: Date

  @Dependency({
    name: 'SaleDateAfterManufactureDate',
    getDependencies: (instance) => ({
      manufactureDate: instance.manufactureDate
    }),
    validate: (saleDate, { manufactureDate }) => saleDate >= manufactureDate,
    message: 'Sale date must be after the manufacture date.'
  })
  public saleDate: Date
}

Dependency Function

Introduction

ValidateDependency is a function that directly validates an object's property against specified dependencies and rules. It's particularly useful in scenarios where validations need to be triggered programmatically.

Usage

Invoke ValidateDependency with the target object, the value to validate, and the validation options object. The function returns a ValidationResult indicating whether the validation passed and containing any error messages if it didn't.

Example

Validating a Product instance's saleDate could look something like this:

typescript
const product = new Product(/* initialize properties */)
const validationResult = ValidateDependency(product, product.saleDate, {
  name: 'SaleDateAfterManufactureDate',
  getDependencies: () => ({ manufactureDate: product.manufactureDate }),
  validate: (saleDate, { manufactureDate }) => saleDate >= manufactureDate,
  message: 'Sale date must be after the manufacture date.'
})

if (!validationResult.isValid) {
  console.error(validationResult.errors)
}

Separating Validation Logic in a Clean Architecture Approach

Overview

This guide focuses on organizing validation logic separately from your model definitions to achieve a cleaner architecture and more maintainable codebase. It explains how to structure your project files and set up validations using a dedicated configuration.

Table of Contents

Folder and File Structure

Organize your project to keep the model definitions clean by following this structure:

src/

├── models/
│   └── AgricultureProduct.ts

├── validations/
│   └── AgricultureProductValidations.ts
  • models/: Contains the application's data models.
  • validations/: Stores separate files for configuring validations for each model.

Setting Up the Model

Define your models in a clear and concise manner, focusing solely on data representation.

AgricultureProduct.ts:

typescript
export class AgricultureProduct {
  public harvestDate: Date
  public saleDate: Date
  // Additional properties as needed.

  constructor(
    harvestDate: Date,
    saleDate: Date
    // Other constructor parameters.
  ) {
    this.harvestDate = harvestDate
    this.saleDate = saleDate
    // Initialize other properties.
  }
}

Configuring Validations

Create dedicated files for validation configurations to decouple validation logic from the model.

AgricultureProductValidations.ts:

typescript
import { Dependency } from 'rest-data-validator'
import { AgricultureProduct } from '../models/AgricultureProduct'

export const configureAgricultureProductValidations = () => {
  Dependency({
    name: 'SaleDateAfterHarvestDate',
    getDependencies: (instance) => ({ harvestDate: instance.harvestDate }),
    validate: (saleDate, { harvestDate }) => saleDate >= harvestDate,
    message: 'The sale date cannot be before the harvest date.'
  })(AgricultureProduct.prototype, 'saleDate')

  // Repeat for other properties needing validation.
}

Applying Configuration

Ensure the validation configurations are applied by invoking the setup function at the application's entry point.

main.ts:

typescript
import 'reflect-metadata'
import { configureAgricultureProductValidations } from './validations/AgricultureProductValidations'

configureAgricultureProductValidations()

Example Usage

With this setup, your AgricultureProduct instances will be validated according to the defined rules, keeping the model's definition clean and focused on its primary purpose.

This approach enhances code maintainability, facilitates easier testing, and adheres to Clean Architecture principles by separating concerns effectively.

Conclusion

Dependency and validateDependency provide a robust framework for implementing complex validation logic that respects the interdependencies between data fields. By following the outlined guidelines and examples, developers can ensure their applications maintain data integrity and adhere to business rules effectively.

Dependency Decorator has loaded